diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/AuthMessageHandler.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/AuthMessageHandler.java index cdd2608b..6d93efd4 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/AuthMessageHandler.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/AuthMessageHandler.java @@ -5,6 +5,7 @@ import cn.iocoder.springboot.lab25.springwebsocket.message.AuthResponse; import cn.iocoder.springboot.lab25.springwebsocket.message.UserJoinNoticeRequest; import cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; import javax.websocket.Session; @@ -13,12 +14,18 @@ public class AuthMessageHandler implements MessageHandler { @Override public void execute(Session session, AuthRequest message) { + // 如果未传递 accessToken + if (StringUtils.isEmpty(message.getAccessToken())) { + WebSocketUtil.send(session, AuthResponse.TYPE, + new AuthResponse().setCode(1).setMessage("认证 accessToken 未传入")); + return; + } + // 添加到 WebSocketUtil 中 - WebSocketUtil.addUser(session, message.getAccessToken()); // 考虑到代码简化,我们先直接使用 accessToken 作为 User + WebSocketUtil.addSession(session, message.getAccessToken()); // 考虑到代码简化,我们先直接使用 accessToken 作为 User // 判断是否认证成功。这里,假装直接成功 - WebSocketUtil.send(session, AuthResponse.TYPE, - new AuthResponse().setCode(0)); + WebSocketUtil.send(session, AuthResponse.TYPE, new AuthResponse().setCode(0)); // 通知所有人,某个人加入了。这个是可选逻辑,仅仅是为了演示 WebSocketUtil.broadcast(UserJoinNoticeRequest.TYPE, diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/MessageHandler.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/MessageHandler.java index 8d0040a4..aa89c13c 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/MessageHandler.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/MessageHandler.java @@ -5,12 +5,21 @@ import cn.iocoder.springboot.lab25.springwebsocket.message.Message; import javax.websocket.Session; /** - * 消息处理器 + * 消息处理器接口 */ public interface MessageHandler { + /** + * 执行处理消息 + * + * @param session 会话 + * @param message 消息 + */ void execute(Session session, T message); + /** + * @return 消息类型,即每个 Message 实现类上的 TYPE 静态字段 + */ String getType(); } diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthRequest.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthRequest.java index 37196bb8..29b8bf11 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthRequest.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthRequest.java @@ -1,7 +1,7 @@ package cn.iocoder.springboot.lab25.springwebsocket.message; /** - * 认证 Message + * 用户认证请求 */ public class AuthRequest implements Message { diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthResponse.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthResponse.java index fbbdccd4..cfa4fbda 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthResponse.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthResponse.java @@ -1,7 +1,7 @@ package cn.iocoder.springboot.lab25.springwebsocket.message; /** - * 认证结果 Message + * 用户认证响应 */ public class AuthResponse implements Message { @@ -11,6 +11,10 @@ public class AuthResponse implements Message { * 响应状态码 */ private Integer code; + /** + * 响应提示 + */ + private String message; public Integer getCode() { return code; @@ -21,4 +25,13 @@ public class AuthResponse implements Message { return this; } + public String getMessage() { + return message; + } + + public AuthResponse setMessage(String message) { + this.message = message; + return this; + } + } diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/Message.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/Message.java index 66978948..974b146a 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/Message.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/Message.java @@ -1,7 +1,7 @@ package cn.iocoder.springboot.lab25.springwebsocket.message; /** - * 基础消息 + * 基础消息体 */ public interface Message { } diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendResponse.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendResponse.java index 37f957f4..c695cf83 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendResponse.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendResponse.java @@ -1,7 +1,7 @@ package cn.iocoder.springboot.lab25.springwebsocket.message; /** - * 发送给单个人消息的成功结果的 Message + * 发送消息响应结果的 Message */ public class SendResponse implements Message { @@ -15,6 +15,10 @@ public class SendResponse implements Message { * 响应状态码 */ private Integer code; + /** + * 响应提示 + */ + private String message; public String getMsgId() { return msgId; @@ -34,4 +38,13 @@ public class SendResponse implements Message { return this; } + public String getMessage() { + return message; + } + + public SendResponse setMessage(String message) { + this.message = message; + return this; + } + } diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToAllRequest.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToAllRequest.java index 6b319995..f75e0f9b 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToAllRequest.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToAllRequest.java @@ -1,7 +1,7 @@ package cn.iocoder.springboot.lab25.springwebsocket.message; /** - * 发送给所有人消息的 Message + * 发送给所有人的群聊消息的 Message */ public class SendToAllRequest implements Message { diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToOneRequest.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToOneRequest.java index 77051364..8daa74ca 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToOneRequest.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToOneRequest.java @@ -1,7 +1,7 @@ package cn.iocoder.springboot.lab25.springwebsocket.message; /** - * 发送给单个人消息的 Message + * 发送给指定人的私聊消息的 Message */ public class SendToOneRequest implements Message { diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToUserRequest.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToUserRequest.java index b9712cc2..67ad15c0 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToUserRequest.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToUserRequest.java @@ -1,5 +1,8 @@ package cn.iocoder.springboot.lab25.springwebsocket.message; +/** + * 发送消息给一个用户的 Message + */ public class SendToUserRequest implements Message { public static final String TYPE = "SEND_TO_USER_REQUEST"; diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/UserJoinNoticeRequest.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/UserJoinNoticeRequest.java index afc24d4c..e1e923af 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/UserJoinNoticeRequest.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/UserJoinNoticeRequest.java @@ -1,7 +1,7 @@ package cn.iocoder.springboot.lab25.springwebsocket.message; /** - * 用户加入通知 Message + * 用户加入群聊的通知 Message */ public class UserJoinNoticeRequest implements Message { diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/util/WebSocketUtil.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/util/WebSocketUtil.java index 3cc70d31..d135a1f7 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/util/WebSocketUtil.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/util/WebSocketUtil.java @@ -18,6 +18,8 @@ public class WebSocketUtil { private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUtil.class); + // ========== 会话相关 ========== + /** * Session 与用户的映射 */ @@ -27,10 +29,24 @@ public class WebSocketUtil { */ private static final Map USER_SESSION_MAP = new ConcurrentHashMap<>(); - public static void addSession(Session session) { - SESSION_USER_MAP.put(session, ""); // 使用 "" 占位,因为 ConcurrentHashMap 不允许 value 为空 + /** + * 添加 Session 。在这个方法中,会添加用户和 Session 之间的映射 + * + * @param session Session + * @param user 用户 + */ + public static void addSession(Session session, String user) { + // 更新 USER_SESSION_MAP + USER_SESSION_MAP.put(user, session); + // 更新 SESSION_USER_MAP + SESSION_USER_MAP.put(session, user); } + /** + * 移除 Session 。 + * + * @param session Session + */ public static void removeSession(Session session) { // 从 SESSION_USER_MAP 中移除 String user = SESSION_USER_MAP.remove(session); @@ -40,13 +56,15 @@ public class WebSocketUtil { } } - public static void addUser(Session session, String user) { - // 更新 USER_SESSION_MAP - USER_SESSION_MAP.put(user, session); - // 更新 SESSION_USER_MAP - SESSION_USER_MAP.put(session, user); - } + // ========== 消息相关 ========== + /** + * 广播发送消息给所有在线用户 + * + * @param type 消息类型 + * @param message 消息体 + * @param 消息类型 + */ public static void broadcast(String type, T message) { // 创建消息 String messageText = buildTextMessage(type, message); @@ -56,6 +74,14 @@ public class WebSocketUtil { } } + /** + * 发送消息给单个用户的 Session + * + * @param session Session + * @param type 消息类型 + * @param message 消息体 + * @param 消息类型 + */ public static void send(Session session, String type, T message) { // 创建消息 String messageText = buildTextMessage(type, message); @@ -63,6 +89,15 @@ public class WebSocketUtil { sendTextMessage(session, messageText); } + /** + * 发送消息给指定用户 + * + * @param user 指定用户 + * @param type 消息类型 + * @param message 消息体 + * @param 消息类型 + * @return 发送是否成功你那个 + */ public static boolean send(String user, String type, T message) { // 获得用户对应的 Session Session session = USER_SESSION_MAP.get(user); @@ -75,6 +110,14 @@ public class WebSocketUtil { return true; } + /** + * 构建完整的消息 + * + * @param type 消息类型 + * @param message 消息体 + * @param 消息类型 + * @return 消息 + */ private static String buildTextMessage(String type, T message) { JSONObject messageObject = new JSONObject(); messageObject.put("type", type); @@ -82,6 +125,12 @@ public class WebSocketUtil { return messageObject.toString(); } + /** + * 真正发送消息 + * + * @param session Session + * @param messageText 消息 + */ private static void sendTextMessage(Session session, String messageText) { if (session == null) { LOGGER.error("[sendTextMessage][session 为 null]"); diff --git a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/websocket/WebsocketServerEndpoint.java b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/websocket/WebsocketServerEndpoint.java index 6700ee73..dde62c33 100644 --- a/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/websocket/WebsocketServerEndpoint.java +++ b/lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/websocket/WebsocketServerEndpoint.java @@ -1,6 +1,7 @@ package cn.iocoder.springboot.lab25.springwebsocket.websocket; import cn.iocoder.springboot.lab25.springwebsocket.handler.MessageHandler; +import cn.iocoder.springboot.lab25.springwebsocket.message.AuthRequest; import cn.iocoder.springboot.lab25.springwebsocket.message.Message; import cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil; import com.alibaba.fastjson.JSON; @@ -12,12 +13,14 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Controller; +import org.springframework.util.CollectionUtils; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -28,9 +31,9 @@ public class WebsocketServerEndpoint implements InitializingBean { private Logger logger = LoggerFactory.getLogger(getClass()); /** - * 设置成静态变量 + * 消息类型与 MessageHandler 的映射 * - * 虽然说 WebsocketServerEndpoint 是单例,但是 Spring Boot 还是会为每个 WebSocket 创建一个 WebsocketServerEndpoint Bean 。 + * 注意,这里设置成静态变量。虽然说 WebsocketServerEndpoint 是单例,但是 Spring Boot 还是会为每个 WebSocket 创建一个 WebsocketServerEndpoint Bean 。 */ private static final Map HANDLERS = new HashMap<>(); @@ -40,8 +43,18 @@ public class WebsocketServerEndpoint implements InitializingBean { @OnOpen public void onOpen(Session session, EndpointConfig config) { logger.info("[onOpen][session({}) 接入]", session); - // 添加到在线缓存 - WebSocketUtil.addSession(session); + // 解析 accessToken + List accessTokenValues = session.getRequestParameterMap().get("accessToken"); + String accessToken = !CollectionUtils.isEmpty(accessTokenValues) ? accessTokenValues.get(0) : null; + // 创建 AuthRequest 消息类型 + AuthRequest authRequest = new AuthRequest().setAccessToken(accessToken); + // 获得消息处理器 + MessageHandler messageHandler = HANDLERS.get(AuthRequest.TYPE); + if (messageHandler == null) { + logger.error("[onOpen][认证消息类型,不存在消息处理器]"); + return; + } + messageHandler.execute(session, authRequest); } @OnMessage