Springboot实现websocket(连接前jwt验证token)

背景

用户连接服务器weksocket前,需经过jwt的token验证(token中包含账号信息),验证合法后,才可以于服务器正常交互。

实现

一、配置依赖(pom.xml)

        <!-- websocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>

二、因为springboot的websocket连接时不会显示header信息,也就无法拿到cookie中的token信息,需要在连接前处理,新建一个WebSocketConfig.class,在连接前做一个jwt的token验证,并获取用户的账号信息添加到session中。

(关于jwt的token验证工具类我这里就不详细讲了,可以去看我的另一篇文章Springboot实现jwt的token验证(超简单)-CSDN博客)

package com.example.springboot.common.config;import com.example.springboot.utils.CharUtil;
import com.example.springboot.utils.CookieUtil;
import com.example.springboot.utils.TokenUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import java.util.List;
import java.util.Map;/*** websocket开启支持*/
@Configuration
public class WebSocketConfig extends ServerEndpointConfig.Configurator {/*** 这个bean会自动注册使用了@ServerEndpoint注解声明的对象* @return*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}/*** 建立握手时,连接前的操作*/@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {// 这个userProperties 可以通过 session.getUserProperties()获取final Map<String, Object> userProperties = sec.getUserProperties();// 获取tokenMap<String, List<String>> headers = request.getHeaders();List<String> cookie = headers.get("cookie");String token = "";if (cookie != null) {token = CookieUtil.getCookie("token", cookie.get(0));}// 判断用户token是否合法,并获取用户id,入果非法,生成一个临时用户用于识别String id = "";try {TokenUtils.verify(token);id = TokenUtils.getTokenInfo(token).getClaim("user").asString();userProperties.put("id", id);} catch (Exception err) {id = "未知用户" + CharUtil.randomVerify();userProperties.put("unknownId", id);}}/*** 初始化端点对象,也就是被@ServerEndpoint所标注的对象*/@Overridepublic <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {return super.getEndpointInstance(clazz);}
}

 三、再新建一个websocket的服务核心类WebSocketServer.class,实现websocket的连接、释放、发送、报错等4大核心功能。

package com.example.springboot.service;import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.example.springboot.common.config.WebSocketConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @author websocket服务*/
@ServerEndpoint(value = "/ws",configurator = WebSocketConfig.class)
@Component
public class WebSocketServer {/*** 打印日志*/private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);/*** 记录当前在线连接数*/public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session) {// 判断是否合法连接,如果不是,直接执行session.close()关闭连接final boolean isverify = openVerify(session);if (isverify) {// 获取用户账号String id = (String) session.getUserProperties().get("id");// 根据账号添加到session列表中sessionMap.put(id, session);log.info("用户ID为={}加入连接, 当前在线人数为:{}", id, sessionMap.size());} else {// 非法连接,进行释放try {session.close();} catch (IOException e) {e.printStackTrace();}}}/*** 后台收到客户端发送过来的消息* @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session session) {// 获取IDString id = (String) session.getUserProperties().get("id");log.info("服务端收到来自用户ID为={}的消息:{}", id, message);// 将JSON数据转换为对象,方便操作JSONObject obj = JSONUtil.parseObj(message);// 储存需要发送的对象JSONArray users = new JSONArray();// 判断是否为群聊,1为否,2为是if (obj.getStr("type").equals("1")) {users.add(obj.getStr("send_id"));users.add(obj.getStr("accept_id"));} else if (obj.getStr("type").equals("2")) {users = obj.getJSONArray("accept_group");}// 判断是否存在发送对象if (users.size() > 0) {for (int i=0;i<users.size();i++){Session toSession = sessionMap.get(users.get(i));if (toSession != null) {this.sendMessage(obj.toString(), toSession);log.info("服务器发送消息给用户ID为={},消息:{}", users.get(i), obj.toString());} else {log.info("发送失败,用户ID为={}未在线", users.get(i));}}} else {log.info("发送对象集合不能为空!");}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose(Session session) {// 判断断开的连接是否是合法的String id = (String) session.getUserProperties().get("id");if (id == "" & id == null) {sessionMap.remove(id);log.info("有一连接正常关闭,移除username={}的用户session, 当前在线人数为:{}", id, sessionMap.size());} else {id = (String) session.getUserProperties().get("unknownId");sessionMap.remove(id);log.info("token验证不通过,移除username={}的用户session, 当前在线人数为:{}", id, sessionMap.size());}}/*** 异常报错* @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("websocket发生异常错误:");error.printStackTrace();}/*** 服务端发送消息给客户端*/private void sendMessage(String message, Session toSession) {try {log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);toSession.getBasicRemote().sendText(message);} catch (Exception e) {log.error("服务端发送消息给客户端失败", e);}}/*** 判断是否是合法用户。是就返回true,反之返回false* @param session* @return*/public static boolean openVerify (Session session) {// 判断是否有合法的idfinal String id = (String) session.getUserProperties().get("id");if (id == "" | id == null) {return false;} else {return true;}}
}

 四、连接路径如下

http://localhost:8899/ws

五、前端发送的JSON数据格式

// 单聊
{"type":1,"content":"你好啊","send_id":"001","accept_id":"002","accept_group":[]
}// 群聊
{"type":2,"content":"大家好啊","send_id":"001","accept_id":"","accept_group":["001","002","003"]
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/147599.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Python实时采集Windows CPU\MEMORY\HDD使用率

文章目录 安装psutil库在Python脚本中导入psutil库获取CPU当前使用率&#xff0c;并打印结果获取内存当前使用率&#xff0c;并打印结果获取磁盘当前使用情况&#xff0c;并打印结果推荐阅读 要通过Python实时采集Windows性能计数器的数据&#xff0c;你可以使用psutil库。psut…

leetCode 376.摆动序列 贪心算法

如果连续数字之间的差严格地在正数和负数之间交替&#xff0c;则数字序列称为 摆动序列 。第一个差&#xff08;如果存在的话&#xff09;可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。 例如&#xff0c; [1, 7, 4, 9, 2, 5] 是一个 摆动序列 &…

QT 之数据库 QSqlQuery CURD 实战

零、参考文档 https://doc.qt.io/archives/qt-6.0/qsqldatabase.html 一、开发环境 Ubuntu 20.04 QT6.0 Microsoft SQL Server 2022 Developer Edition (64-bit) 先修改 /etc/odbc.ini 的数据源配置&#xff0c;指定连接数据库 vdb&#xff0c; sudo vim /etc/odbc.ini[mss…

基于SpringBoot的社区医院管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

程序员的重复劳动陷阱

https://kb.cnblogs.com/page/627035/ 同样是一样的计算机专业毕业&#xff0c;进入职场的职位和工作都差不多&#xff0c;为何有些程序员短短几年就成长为全能选手或领域专家&#xff0c;有些程序员还在做CRUD&#xff1f; 程序员的重复劳动陷阱 不知道大家有没有这样的感觉…

leetCode 121. 买卖股票的最佳时机 贪心算法

给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的最大利润。…

SpringBoot注册web组件

目录 前言 一、注册Servlet组件 1.1 使用SpringBoot注解加继承HttpServet类注册 1.2 通过继承HttpServet类加配置类来进行注册 二、注册Listener组件 2.1 使用SpringBoot注解和实现ServletContextListener接口注册 2.2 ServletContextListener接口和配置类来进行注册 …

Linux shell编程学习笔记6:查看和设置变量的常用命令

上节我们介绍了变量的变量命名规则、变量类型、使用变量时要注意的事项&#xff0c;今天我们学习一下查看和设置变量的一些常用命令&#xff0c;包括变量的提升&#xff0c;有些命令在之前的实例中已经使用过了。 一、 echo &#xff1a;查看变量的值 语法格式&#xff1a;ech…

PCL 计算点云中值

目录 一、算法原理2、主要函数二、代码实现三、结果展示四、参考链接本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 计算点云坐标的中值点,首先对点云坐标进行排序,然后计算中值。如果点云点的个数为奇数…

【iptables 实战】07 iptables NAT实验

在上一节中&#xff0c;我们将两个网段的机器&#xff0c;通过中间机器的网络转发&#xff0c;能达到互通。再来回顾一下这个网络连接的图 上一节我们在防火墙实验中&#xff0c;设置了主机B的的转发规则&#xff0c;我们先清空主机B的转发规则 [rootlocalhost ~]# iptables…

springboot整合es

springboot整合es 1.引入依赖&#xff08;springboot2.3.x版本可以兼容elasticsearch7.x版本。&#xff09; <parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.3.6.R…

记录:Unity脚本的编写2.0

目录 前言控制方法键盘控制鼠标控制虚拟控制器控制 平移和旋转 前言 前面记录了一些简单的unity脚本用来控制unity中对象模型的移动&#xff08;或者不能叫控制&#xff0c;毕竟它是开启之后自己在跑的&#xff09;&#xff0c;那么让模型可以根据用户的操作来进行变化的方法自…

vue 使用 创建二维数组响应数据 渲染 echarts图标

目前我遇到的情况就是用动态的二维数组数据渲染echarts图标&#xff0c;我们从后端收到的接口一般是个一维数组&#xff0c;需要手动构建并且保证响应式。接下来我做了个案例 一、案例总逻辑 1. 先创建一个vue项目 2. 添加 echarts依赖 3. 模拟数据请求&#xff0c;构建二维数组…

串口数据包收发

数据包 把属于同一批的数据进行打包和分割&#xff0c;方便接收方进行识别 HEX数据包 思路&#xff1a;一个数据规定四个字节&#xff0c;以0xFF为包头&#xff0c;0xFE为包尾&#xff0c;当检测到0xFF时&#xff0c;接下来四个数据就是数据&#xff0c;接收到0xFE时&#x…

Win10系统中GPU深度学习环境配置记录

运行环境 系统&#xff1a;Win10 处理器 Intel(R) Core(TM) i7-9700K CPU 3.60GHz 3.60 GHz 机带 RAM 16.0 GB 设备 ID A18D4ED3-8CA1-4DC6-A6EF-04A33043A5EF 产品 ID 00342-35285-64508-AAOEM 系统类型 64 位操作系统, 基于 x64 的处理器 显卡&#xff1a;NVIDIA GeF…

pycharm一直没显示运行步骤,只是出现waiting for process detach

pycharm一直没显示运行步骤&#xff0c;只是出现waiting for process detach&#xff1b;各类音乐免费软件&#xff1b;最棒的下载torch-geometric-CSDN博客&#xff08;不太推荐&#xff09;我强烈推荐这个&#xff1a;_waiting for process detachhttps://blog.csdn.net/weix…

2023年汉字小达人市级比赛在线模拟题来了,四种练习助力好成绩

2023年第十届汉字小达人比赛区级自由报名活动已于9月30日结束&#xff0c;尽管最终晋级市级比赛的名单还需要在11月初发布&#xff08;有一些学校的校级选拔还没结束&#xff09;&#xff0c;但是许多小朋友已经开始准备市级比赛了。 根据往年的经验&#xff0c;实际比赛也是在…

Android 活动Activity

目录 一、启停活动页面1.1 Activity的启动和结束1.2 Activity的生命周期1.3 Activity的启动模式 二、在活动之间传递消息2.1 显式Intent和隐式Intent2.2 向下一个Activity发送数据2.3 向上一个Activity返回数据 三、补充附加信息3.1 利用资源文件配置字符串3.2 利用元数据传递配…

国庆加速度!新增功能点锁定功能,敏捷开发新增估算功能,助力项目快速突破!

大家好&#xff0c;CoCode开发云旗下Co-Project V3.6智能项目管理平台正式发布&#xff0c;平台新增功能点锁定功能、敏捷开发模式新增估算板块和两种估算方式。 功能点锁定功能进一步提高了项目估算的灵活性和准确性&#xff0c;有利于提高项目估算效率&#xff1b;而敏捷开发…

数据分析:人工智能篇

文章目录 第三章 数据可视化库matplotlib3.1 matplotlib基本绘图操作3.2 plot的线条和颜色3.3 条形图分析3.4 箱型图分析3.5 直方图分析3.6 散点图分析3.7 图表的美化 第四章 数据预测库Sklearn4.1 sklearn预测未来4.2 回归数据的预测4.2.1 回归数据的切分4.2.2 线性回归数据模…