赞
踩
添加链接描述开发一个多人聊天室的 demo,包含以下功能
1.用户注册功能:注册成功后持久化用户信息到数据库
2.用户登录功能:校验用户信息成功后,成功登录并且返回 token 信息和缓存至 Redis
3.多人聊天功能 :携带 token 链接 WebSocet 并且链接时校验 token 是否合法,链接成功后进入聊天室可以正常聊天
4.单客户端登录:一个账号只能一个客户端,后登录的客户端需把前面一个客户端踢下线,效果如下:在客户端 A 中用户 1 登录成功并且携带 token 链接 WebSocket 后继续在客户端 B 登录用户 1 账号拿到 token 也链接 WebSocet,这时客户端 A 需要接收到 WebSocket 发来踢下线消息
UserController
@RestController @RequestMapping("api/v1/user") public class UserController { @Autowired UserService userService; /** * 注册 * @param userDto * @return */ @PostMapping("/register") public JsonData register(@RequestBody UserDto userDto){ JsonData jsonData = userService.register(userDto); return jsonData; } /** * 登录 * @param userDto * @return */ @PostMapping("/login") public JsonData login(@RequestBody UserDto userDto){ User user = userService.login(userDto); String token = userService.geneAccountToken(user); return JsonData.buildSuccess(token); } }
UserServiceImpl
@RestController @RequestMapping("api/v1/user") public class UserController { @Autowired UserService userService; /** * 注册 * @param userDto * @return */ @PostMapping("/register") public JsonData register(@RequestBody UserDto userDto){ JsonData jsonData = userService.register(userDto); return jsonData; } /** * 登录 * @param userDto * @return */ @PostMapping("/login") public JsonData login(@RequestBody UserDto userDto){ User user = userService.login(userDto); String token = userService.geneAccountToken(user); return JsonData.buildSuccess(token); } }
JWTUtil
@Slf4j public class JWTUtil { /** * 主题 */ private static final String SUBJECT = "luo"; //客户端加密密钥 private static final String CLIENT_SECRET = "123abc"; //用户令牌前缀 private static final String CLIENT_TOKNE_PREFIX = "abc"; /** * 用户token过期时间,7天 */ public static final long CLIENT_EXPIRED = 1000 * 60 * 60 * 24 * 7; /** * 生成token * @param loginAccount * @return */ public static String geneClientJsonWebToken(LoginAccount loginAccount) { if (loginAccount == null) { throw new NullPointerException("对象参数为空"); } log.info("loginAccount:{}",loginAccount); String token = Jwts.builder().setSubject(SUBJECT) //配置payload .claim("id",loginAccount.getId()) .claim("username", loginAccount.getUsername()) .claim("password", loginAccount.getPassword()) .claim("loginDateTime", new Date()) .signWith(SignatureAlgorithm.HS256, CLIENT_SECRET).compact(); //生成token token = CLIENT_TOKNE_PREFIX + token; return token; } /** * 解密token * @param token * @return */ public static Claims checkClientJWT(String token){ try { final Claims claims =Jwts.parser().setSigningKey(CLIENT_SECRET) .parseClaimsJws(token.replace(CLIENT_TOKNE_PREFIX,"")).getBody(); return claims; }catch (Exception e){ log.error("解密失败"); return null; } }
WebSocketService
/** * @author :lwj * @date : 2023/3/23 */ @Slf4j @Component @ServerEndpoint("/websocket/{token}") public class WebSocketServer { /**concurrent包的线程安全集合,也可以map改成set,用来存放每个客户端对应的MyWebSocket对象。*/ public static ConcurrentHashMap<String, Session> webSocketMap = new ConcurrentHashMap<>(); /** * 连接建立时 */ @OnOpen public void onOpen(@PathParam("token") String token, Session session) { //检验token是否合法 try { //校验token是否合法 Claims claims = JWTUtil.checkClientJWT(token); //获取用户id String id = claims.get("id").toString(); //判断用户是否登录 if (webSocketMap.containsKey(id)){ //向断开连接的客户端发送消息 sendMessage(webSocketMap.get(id),"您已经被下线"); webSocketMap.get(id).close(); webSocketMap.remove(id); //再次连接 webSocketMap.put(id,session); log.info("用户登录:{}", id); }else { log.info("用户登录:{}", id); //将自己的信息添加到map集合中 webSocketMap.put(id, session); } } catch (Exception e) { log.error("解析token失败"); } } /** * 客户端接收服务端发来的数据时 */ @OnMessage public void onMessage (@PathParam("token") String token, String message) throws IOException { //校验token是否合法 Claims claims = JWTUtil.checkClientJWT(token); //获取用户id String id = claims.get("id").toString(); log.info("【websocket消息】收到客户端用户{}发来的消息:{}", id,message); sendMessage(webSocketMap.get(id),message); log.info("发送消息:{}, {}", id, message); } /** * 连接关闭 */ @OnClose public void onClose (@PathParam("token") String token, Session session){ //校验token是否合法 Claims claims = JWTUtil.checkClientJWT(token); //获取用户id String id = claims.get("id").toString(); //从当前的map集合中移除该用户 webSocketMap.remove(id); try { session.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 聊天发生错误 */ @OnError public void onError (Session session, Throwable error){ try { //关闭WebSocket下的该Seesion会话 session.close(); } catch (IOException e) { log.error("错误原因:"+error.getMessage()); e.printStackTrace(); } } /** * 使用连接发送消息 * @param session 用户的session * @param message 发送的消息内容 */ public void sendMessage(Session session,String message) throws IOException { session.getBasicRemote().sendText(message); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。