使用WebSocket实现的一个小Demo

添加链接描述开发一个多人聊天室的 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);
    }
}

源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值