mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-20 02:08:15 +08:00
update 优化并发登录用户只能接收到一个ws长连接消息
This commit is contained in:
parent
2e08825da8
commit
20af1c9acc
@ -95,11 +95,11 @@ public class AuthController {
|
||||
// 登录
|
||||
LoginVo loginVo = IAuthStrategy.login(body, client, grantType);
|
||||
|
||||
Long userId = LoginHelper.getUserId();
|
||||
String tokenId = LoginHelper.getTokenId();
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
WebSocketMessageDto dto = new WebSocketMessageDto();
|
||||
dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统");
|
||||
dto.setSessionKeys(List.of(userId));
|
||||
dto.setSessionKeys(List.of(tokenId));
|
||||
WebSocketUtils.publishMessage(dto);
|
||||
}, 3, TimeUnit.SECONDS);
|
||||
return R.ok(loginVo);
|
||||
@ -198,7 +198,7 @@ public class AuthController {
|
||||
}
|
||||
// 根据域名进行筛选
|
||||
List<TenantListVo> list = StreamUtils.filter(voList, vo ->
|
||||
StringUtils.equals(vo.getDomain(), host));
|
||||
StringUtils.equals(vo.getDomain(), host));
|
||||
// 返回对象
|
||||
LoginTenantVo vo = new LoginTenantVo();
|
||||
vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
|
||||
|
||||
@ -3,6 +3,7 @@ package org.dromara.common.satoken.utils;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.AccessLevel;
|
||||
@ -10,9 +11,13 @@ import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.constant.TenantConstants;
|
||||
import org.dromara.common.core.constant.UserConstants;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.core.enums.DeviceType;
|
||||
import org.dromara.common.core.enums.UserType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 登录鉴权助手
|
||||
@ -69,6 +74,54 @@ public class LoginHelper {
|
||||
return (LoginUser) session.get(LOGIN_USER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户的token
|
||||
*/
|
||||
public static String getTokenId() {
|
||||
return StpUtil.getTokenInfo().getTokenValue() ;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户的 token 集合
|
||||
*/
|
||||
public static List<String> getTokenIds() {
|
||||
return StpUtil.getTokenValueListByLoginId(getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户 指定设备类型端的 token 集合
|
||||
*/
|
||||
public static List<String> getTokenIds(DeviceType device) {
|
||||
return StpUtil.getTokenValueListByLoginId(getUserId(), device.getDevice());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号 userId 的 token 集合
|
||||
*/
|
||||
public static List<String> getTokenIds(Long userId) {
|
||||
return StpUtil.getTokenValueListByLoginId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号 userIds 的 token 集合
|
||||
*/
|
||||
public static List<String> getTokenUserIds(Set<Long> userIds) {
|
||||
if (CollUtil.isNotEmpty(userIds)) {
|
||||
return userIds.stream()
|
||||
.flatMap(userId -> StpUtil.getTokenValueListByLoginId(userId).stream())
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定账号 userId 指定设备类型端的 token 集合
|
||||
*/
|
||||
public static List<String> getTokenIds(Long userId, DeviceType device) {
|
||||
return StpUtil.getTokenValueListByLoginId(userId, device.getDevice());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户基于token
|
||||
*/
|
||||
|
||||
@ -6,10 +6,12 @@ package org.dromara.common.websocket.constant;
|
||||
* @author zendwang
|
||||
*/
|
||||
public interface WebSocketConstants {
|
||||
|
||||
/**
|
||||
* websocketSession中的参数的key
|
||||
*/
|
||||
String LOGIN_USER_KEY = "loginUser";
|
||||
String TOKEN_ID_KEY = "tokenId";
|
||||
|
||||
/**
|
||||
* 订阅的频道
|
||||
|
||||
@ -18,9 +18,9 @@ public class WebSocketMessageDto implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 需要推送到的session key 列表
|
||||
* 需要推送到的session key 列表 一般为用户tokenId
|
||||
*/
|
||||
private List<Long> sessionKeys;
|
||||
private List<String> sessionKeys;
|
||||
|
||||
/**
|
||||
* 需要发送的消息
|
||||
|
||||
@ -2,6 +2,7 @@ package org.dromara.common.websocket.handler;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.websocket.dto.WebSocketMessageDto;
|
||||
import org.dromara.common.websocket.holder.WebSocketSessionHolder;
|
||||
import org.dromara.common.websocket.utils.WebSocketUtils;
|
||||
@ -11,6 +12,7 @@ import org.springframework.web.socket.handler.AbstractWebSocketHandler;
|
||||
import java.util.List;
|
||||
|
||||
import static org.dromara.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
|
||||
import static org.dromara.common.websocket.constant.WebSocketConstants.TOKEN_ID_KEY;
|
||||
|
||||
/**
|
||||
* WebSocketHandler 实现类
|
||||
@ -26,7 +28,8 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) {
|
||||
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
|
||||
WebSocketSessionHolder.addSession(loginUser.getUserId(), session);
|
||||
String tokenId = (String) session.getAttributes().get(TOKEN_ID_KEY);
|
||||
WebSocketSessionHolder.addSession(tokenId, session);
|
||||
log.info("[connect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
|
||||
}
|
||||
|
||||
@ -40,9 +43,9 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
|
||||
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
|
||||
List<Long> userIds = List.of(loginUser.getUserId());
|
||||
List<String> tokenIds = LoginHelper.getTokenIds(loginUser.getUserId());
|
||||
WebSocketMessageDto webSocketMessageDto = new WebSocketMessageDto();
|
||||
webSocketMessageDto.setSessionKeys(userIds);
|
||||
webSocketMessageDto.setSessionKeys(tokenIds);
|
||||
webSocketMessageDto.setMessage(message.getPayload());
|
||||
WebSocketUtils.publishMessage(webSocketMessageDto);
|
||||
}
|
||||
@ -85,7 +88,8 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
|
||||
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
|
||||
WebSocketSessionHolder.removeSession(loginUser.getUserId());
|
||||
String tokenId = (String) session.getAttributes().get(TOKEN_ID_KEY);
|
||||
WebSocketSessionHolder.removeSession(tokenId);
|
||||
log.info("[disconnect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
|
||||
}
|
||||
|
||||
|
||||
@ -16,27 +16,31 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class WebSocketSessionHolder {
|
||||
|
||||
private static final Map<Long, WebSocketSession> USER_SESSION_MAP = new ConcurrentHashMap<>();
|
||||
private static final Map<String, WebSocketSession> USER_SESSION_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
public static void addSession(Long sessionKey, WebSocketSession session) {
|
||||
/**
|
||||
* @param sessionKey session主键 一般为用户tokenId
|
||||
* @param session 会话
|
||||
*/
|
||||
public static void addSession(String sessionKey, WebSocketSession session) {
|
||||
USER_SESSION_MAP.put(sessionKey, session);
|
||||
}
|
||||
|
||||
public static void removeSession(Long sessionKey) {
|
||||
public static void removeSession(String sessionKey) {
|
||||
if (USER_SESSION_MAP.containsKey(sessionKey)) {
|
||||
USER_SESSION_MAP.remove(sessionKey);
|
||||
}
|
||||
}
|
||||
|
||||
public static WebSocketSession getSessions(Long sessionKey) {
|
||||
public static WebSocketSession getSessions(String sessionKey) {
|
||||
return USER_SESSION_MAP.get(sessionKey);
|
||||
}
|
||||
|
||||
public static Set<Long> getSessionsAll() {
|
||||
public static Set<String> getSessionsAll() {
|
||||
return USER_SESSION_MAP.keySet();
|
||||
}
|
||||
|
||||
public static Boolean existSession(Long sessionKey) {
|
||||
public static Boolean existSession(String sessionKey) {
|
||||
return USER_SESSION_MAP.containsKey(sessionKey);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package org.dromara.common.websocket.interceptor;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.model.LoginUser;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
@ -11,6 +11,7 @@ import org.springframework.web.socket.server.HandshakeInterceptor;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.dromara.common.websocket.constant.WebSocketConstants.LOGIN_USER_KEY;
|
||||
import static org.dromara.common.websocket.constant.WebSocketConstants.TOKEN_ID_KEY;
|
||||
|
||||
/**
|
||||
* WebSocket握手请求的拦截器
|
||||
@ -32,7 +33,9 @@ public class PlusWebSocketInterceptor implements HandshakeInterceptor {
|
||||
@Override
|
||||
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
String tokenId = LoginHelper.getTokenId();
|
||||
attributes.put(LOGIN_USER_KEY, loginUser);
|
||||
attributes.put(TOKEN_ID_KEY, tokenId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -31,10 +31,10 @@ public class WebSocketUtils {
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param sessionKey session主键 一般为用户id
|
||||
* @param sessionKey session主键 一般为用户tokenId
|
||||
* @param message 消息文本
|
||||
*/
|
||||
public static void sendMessage(Long sessionKey, String message) {
|
||||
public static void sendMessage(String sessionKey, String message) {
|
||||
WebSocketSession session = WebSocketSessionHolder.getSessions(sessionKey);
|
||||
sendMessage(session, message);
|
||||
}
|
||||
@ -54,9 +54,9 @@ public class WebSocketUtils {
|
||||
* @param webSocketMessage 消息对象
|
||||
*/
|
||||
public static void publishMessage(WebSocketMessageDto webSocketMessage) {
|
||||
List<Long> unsentSessionKeys = new ArrayList<>();
|
||||
List<String> unsentSessionKeys = new ArrayList<>();
|
||||
// 当前服务内session,直接发送消息
|
||||
for (Long sessionKey : webSocketMessage.getSessionKeys()) {
|
||||
for (String sessionKey : webSocketMessage.getSessionKeys()) {
|
||||
if (WebSocketSessionHolder.existSession(sessionKey)) {
|
||||
WebSocketUtils.sendMessage(sessionKey, webSocketMessage.getMessage());
|
||||
continue;
|
||||
|
||||
@ -12,6 +12,7 @@ import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
|
||||
import org.dromara.common.mail.utils.MailUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.tenant.helper.TenantHelper;
|
||||
import org.dromara.common.websocket.dto.WebSocketMessageDto;
|
||||
import org.dromara.common.websocket.utils.WebSocketUtils;
|
||||
@ -324,7 +325,7 @@ public class WorkflowUtils {
|
||||
switch (messageTypeEnum) {
|
||||
case SYSTEM_MESSAGE:
|
||||
WebSocketMessageDto dto = new WebSocketMessageDto();
|
||||
dto.setSessionKeys(new ArrayList<>(userIds));
|
||||
dto.setSessionKeys(LoginHelper.getTokenUserIds(userIds));
|
||||
dto.setMessage(message);
|
||||
WebSocketUtils.publishMessage(dto);
|
||||
break;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user