权限控制
This commit is contained in:
@@ -64,6 +64,31 @@
|
||||
<version>21.6.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.gather.event.transientes.controller;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.gather.event.transientes.websocket.WebSocketServer;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* @Author: cdf
|
||||
* @CreateTime: 2025-06-23
|
||||
* @Description:
|
||||
*/
|
||||
@Api(tags = "暂降接收")
|
||||
@RequestMapping("accept")
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class EventGateController extends BaseController {
|
||||
|
||||
private final WebSocketServer webSocketServer;
|
||||
|
||||
|
||||
@OperateInfo
|
||||
@GetMapping("/eventMsg")
|
||||
@ApiOperation("接收远程推送的暂态事件")
|
||||
@ApiImplicitParam(name = "eventMsg", value = "暂态事件json字符", required = true)
|
||||
public HttpResult<JSONObject> eventMsg(@RequestParam("msg") String msg) {
|
||||
String methodDescribe = getMethodDescribe("eventMsg");
|
||||
System.out.println(msg);
|
||||
|
||||
JSONObject jsonObject = new JSONObject(msg);
|
||||
webSocketServer.sendMessageToUser("bbb",jsonObject.toString());
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, jsonObject, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -78,4 +78,16 @@ public class LargeScreenCountController extends BaseController {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.njcn.gather.event.transientes.filter;
|
||||
|
||||
import com.njcn.gather.event.transientes.utils.JwtUtil;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class JwtRequestFilter extends OncePerRequestFilter {
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
private final JwtUtil jwtUtil;
|
||||
|
||||
public JwtRequestFilter(UserDetailsService userDetailsService, JwtUtil jwtUtil) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.jwtUtil = jwtUtil;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
final String authorizationHeader = request.getHeader("Authorization");
|
||||
|
||||
String username = null;
|
||||
String jwt = null;
|
||||
|
||||
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
|
||||
jwt = authorizationHeader.substring(7);
|
||||
try {
|
||||
username = jwtUtil.extractUsername(jwt);
|
||||
} catch (Exception e) {
|
||||
// 可以在这里处理令牌过期的情况
|
||||
log.info("JWT Token 校验异常,可能过期了");
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.getWriter().write("Token expired");
|
||||
return; // 终止请求处理
|
||||
}
|
||||
}
|
||||
|
||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
|
||||
|
||||
if (jwtUtil.validateToken(jwt, userDetails)) {
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(
|
||||
userDetails, null, userDetails.getAuthorities());
|
||||
usernamePasswordAuthenticationToken
|
||||
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
|
||||
}
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.njcn.gather.event.transientes.security;
|
||||
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.event.transientes.utils.JwtUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class AuthController {
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@PostMapping("/cn_authenticate")
|
||||
public String createAuthenticationToken(@RequestBody AuthRequest authRequest) throws Exception {
|
||||
try {
|
||||
authenticationManager.authenticate(
|
||||
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
|
||||
);
|
||||
} catch (BadCredentialsException e) {
|
||||
e.printStackTrace();
|
||||
throw new BusinessException("Incorrect username or password");
|
||||
}
|
||||
final UserDetails userDetails = userDetailsService.loadUserByUsername(authRequest.getUsername());
|
||||
final String jwt = jwtUtil.generateToken(userDetails);
|
||||
return jwt;
|
||||
}
|
||||
}
|
||||
|
||||
// 认证请求类
|
||||
class AuthRequest {
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.gather.event.transientes.security;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MyUserDetailsService implements UserDetailsService {
|
||||
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
// 这里应该从数据库中获取用户信息,本示例使用硬编码用户
|
||||
if ("cdf".equals(username)) {
|
||||
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
String encodedPassword = passwordEncoder.encode("@#001njcnpqs");
|
||||
return new User("cdf", encodedPassword,
|
||||
new ArrayList<>());
|
||||
}else if("screen".equals(username)){
|
||||
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
String encodedPassword = passwordEncoder.encode("@#001njcnpqs");
|
||||
return new User("screen", encodedPassword,
|
||||
new ArrayList<>());
|
||||
} else {
|
||||
throw new UsernameNotFoundException("User not found with username: " + username);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.njcn.gather.event.transientes.security;
|
||||
|
||||
import com.njcn.gather.event.transientes.filter.JwtRequestFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Autowired
|
||||
private JwtRequestFilter jwtRequestFilter;
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/cn_authenticate").permitAll() // 允许访问认证接口
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 使用无状态会话
|
||||
|
||||
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.njcn.gather.event.transientes.utils;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Component
|
||||
public class JwtUtil {
|
||||
|
||||
private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
|
||||
private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 100000L; // 10小时
|
||||
|
||||
// 生成JWT令牌
|
||||
public String generateToken(UserDetails userDetails) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
return createToken(claims, userDetails.getUsername());
|
||||
}
|
||||
|
||||
private String createToken(Map<String, Object> claims, String subject) {
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setSubject(subject)
|
||||
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
|
||||
.signWith(SECRET_KEY, SignatureAlgorithm.HS256)
|
||||
.compact();
|
||||
}
|
||||
|
||||
// 验证令牌
|
||||
public Boolean validateToken(String token, UserDetails userDetails) {
|
||||
final String username = extractUsername(token);
|
||||
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
||||
}
|
||||
|
||||
// 提取用户名
|
||||
public String extractUsername(String token) {
|
||||
return extractClaim(token, Claims::getSubject);
|
||||
}
|
||||
|
||||
// 提取过期时间
|
||||
public Date extractExpiration(String token) {
|
||||
return extractClaim(token, Claims::getExpiration);
|
||||
}
|
||||
|
||||
private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
|
||||
final Claims claims = extractAllClaims(token);
|
||||
return claimsResolver.apply(claims);
|
||||
}
|
||||
|
||||
private Claims extractAllClaims(String token) {
|
||||
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
|
||||
}
|
||||
|
||||
private Boolean isTokenExpired(String token) {
|
||||
return extractExpiration(token).before(new Date());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.njcn.gather.event.transientes.websocket;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -12,6 +13,9 @@ import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Description:
|
||||
@@ -22,156 +26,105 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ServerEndpoint(value ="/api/pushMessage/{userIdAndLineIdAndDevId}")
|
||||
@ServerEndpoint(value = "/ws/{userId}")
|
||||
public class WebSocketServer {
|
||||
|
||||
/**
|
||||
* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
|
||||
*/
|
||||
private static int onlineCount = 0;
|
||||
/**
|
||||
* concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。
|
||||
*/
|
||||
private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
|
||||
*/
|
||||
private Session session;
|
||||
/**
|
||||
* 接收userId
|
||||
*/
|
||||
private String userIdAndLineIdAndDevId = "";
|
||||
private static final ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
|
||||
private final ScheduledExecutorService heartbeatExecutor = Executors.newScheduledThreadPool(1);
|
||||
|
||||
/**
|
||||
* 连接建立成
|
||||
* 功调用的方法
|
||||
*/
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam("userIdAndLineIdAndDevId") String userIdAndLineIdAndDevId) {
|
||||
this.session = session;
|
||||
this.userIdAndLineIdAndDevId = userIdAndLineIdAndDevId;
|
||||
if (webSocketMap.containsKey(userIdAndLineIdAndDevId)) {
|
||||
webSocketMap.remove(userIdAndLineIdAndDevId);
|
||||
//加入set中
|
||||
webSocketMap.put(userIdAndLineIdAndDevId, this);
|
||||
public void onOpen(Session session, @PathParam("userId") String userId) {
|
||||
if (StrUtil.isNotBlank(userId)) {
|
||||
sessions.put(userId, session);
|
||||
sendMessage(session, "连接成功");
|
||||
System.out.println("用户 " + userId + " 已连接");
|
||||
|
||||
// 启动心跳检测
|
||||
startHeartbeat(session, userId);
|
||||
} else {
|
||||
//加入set中
|
||||
webSocketMap.put(userIdAndLineIdAndDevId, this);
|
||||
//在线数加1
|
||||
addOnlineCount();
|
||||
try {
|
||||
session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "用户ID不能为空"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
sendMessage("连接成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接关闭
|
||||
* 调用的方法
|
||||
*/
|
||||
@OnClose
|
||||
public void onClose() {
|
||||
if (webSocketMap.containsKey(userIdAndLineIdAndDevId)) {
|
||||
webSocketMap.remove(userIdAndLineIdAndDevId);
|
||||
//从set中删除
|
||||
subOnlineCount();
|
||||
}
|
||||
log.info("监测点退出:" + userIdAndLineIdAndDevId + ",当前在线监测点数为:" + getOnlineCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到客户端消
|
||||
* 息后调用的方法
|
||||
*
|
||||
* @param message 客户端发送过来的消息
|
||||
**/
|
||||
@OnMessage
|
||||
public void onMessage(String message, Session session) {
|
||||
//会每30s发送请求1次
|
||||
log.info("监测点消息:" + userIdAndLineIdAndDevId + ",报文:" + message);
|
||||
log.info("监测点连接:" + userIdAndLineIdAndDevId + ",当前在线监测点数为:" + getOnlineCount());
|
||||
|
||||
public void onMessage(String message, Session session, @PathParam("userId") String userId) {
|
||||
if ("ping".equalsIgnoreCase(message)) {
|
||||
// 处理心跳请求
|
||||
sendMessage(session, "pong");
|
||||
} else {
|
||||
// 处理业务消息
|
||||
System.out.println("收到用户 " + userId + " 的消息: " + message);
|
||||
// TODO: 处理业务逻辑
|
||||
}
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose(Session session, CloseReason closeReason, @PathParam("userId") String userId) {
|
||||
sessions.remove(userId);
|
||||
System.out.println("用户 " + userId + " 已断开连接,状态码: " + closeReason.getReasonPhrase());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param session
|
||||
* @param error
|
||||
*/
|
||||
@OnError
|
||||
public void onError(Session session, Throwable error) {
|
||||
|
||||
log.error("监测点错误:" + this.userIdAndLineIdAndDevId + ",原因:" + error.getMessage());
|
||||
error.printStackTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现服务
|
||||
* 器主动推送
|
||||
*/
|
||||
public void sendMessage(String message) {
|
||||
public void onError(Session session, Throwable throwable, @PathParam("userId") String userId) {
|
||||
System.out.println("用户 " + userId + " 发生错误: " + throwable.getMessage());
|
||||
try {
|
||||
this.session.getBasicRemote().sendText(message);
|
||||
session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, "发生错误"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送自定
|
||||
* 义消息
|
||||
**/
|
||||
public static void sendInfo(String message, String lineId) {
|
||||
log.info("发送消息到:" + lineId + ",报文:" + message);
|
||||
if (StringUtils.isNotBlank(lineId)) {
|
||||
Map<String, String> stringStringMap = WebSocketServer.filterMapByKey(webSocketMap, lineId);
|
||||
stringStringMap.forEach((k,v)->{
|
||||
webSocketMap.get(k).sendMessage(message);
|
||||
|
||||
});
|
||||
} else {
|
||||
log.error("监测点" + lineId + ",不在线!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得此时的
|
||||
* 在线监测点
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static synchronized int getOnlineCount() {
|
||||
return onlineCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线监测点
|
||||
* 数加1
|
||||
*/
|
||||
public static synchronized void addOnlineCount() {
|
||||
WebSocketServer.onlineCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线监测点
|
||||
* 数减1
|
||||
*/
|
||||
public static synchronized void subOnlineCount() {
|
||||
WebSocketServer.onlineCount--;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤所有键包含指定字符串的条目
|
||||
* @param map 原始的Map
|
||||
* @param substring 要检查的子字符串
|
||||
* @return 过滤的Map
|
||||
*/
|
||||
public static Map<String, String> filterMapByKey(ConcurrentHashMap<String, WebSocketServer> map, String substring) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (Map.Entry<String, WebSocketServer> entry : map.entrySet()) {
|
||||
if (entry.getKey().contains(substring)) {
|
||||
result.put(entry.getKey(), entry.getValue().toString());
|
||||
public void sendMessageToUser(String userId, String message) {
|
||||
Session session = sessions.get(userId);
|
||||
if (session != null && session.isOpen()) {
|
||||
try {
|
||||
session.getBasicRemote().sendText(message);
|
||||
} catch (IOException e) {
|
||||
System.out.println("发送消息给用户 " + userId + " 失败: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
System.out.println("用户 " + userId + " 不在线或会话已关闭");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void sendMessageToAll(String message) {
|
||||
sessions.forEach((userId, session) -> {
|
||||
if (session.isOpen()) {
|
||||
try {
|
||||
session.getBasicRemote().sendText(message);
|
||||
} catch (IOException e) {
|
||||
System.out.println("发送消息给用户 " + userId + " 失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendMessage(Session session, String message) {
|
||||
try {
|
||||
session.getBasicRemote().sendText(message);
|
||||
} catch (IOException e) {
|
||||
System.out.println("发送消息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void startHeartbeat(Session session, String userId) {
|
||||
heartbeatExecutor.scheduleAtFixedRate(() -> {
|
||||
if (session.isOpen()) {
|
||||
try {
|
||||
session.getBasicRemote().sendText("ping");
|
||||
} catch (IOException e) {
|
||||
System.out.println("发送心跳给用户 " + userId + " 失败: " + e.getMessage());
|
||||
try {
|
||||
session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, "心跳失败"));
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 10, 10, TimeUnit.SECONDS); // 每10秒发送一次心跳
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user