Spring WebSocket 应用,鉴权token,多个页面访问同一个websocket

发布于:2023-01-22 ⋅ 阅读:(6) ⋅ 点赞:(0) ⋅ 评论:(0)

目录

1、SpringBoot+WebSocket没有token鉴权

2、WebSocket通过url鉴权token (postman可测试成功)

3、WebSocket通过 自定义协议 "sec-websocket-protocol"鉴权

        

         4、解决困扰我很久的问题


本人是看过这些博客后,在实际应用中总结出来的,遇到的问题也很多,但是站在巨人的肩膀上就是会少走弯路!!!感谢!!

spring boot 集成 websocket 的四种方式 - 简书

实战 | spring boot 集成 websocket 的四种方式_mb6140060e201b0的技术博客_51CTO博客

WebSocket实现鉴权方案__alone_的博客-CSDN博客_websocket 鉴权

cookie、token与xss、csrf攻击 - 下一秒钟已经不同 - 博客园

1、SpringBoot+WebSocket没有token鉴权

原生注解部分

①、pom文件——注入websocket依赖;

②、websocketConfig配置文件——添加ServerEndpointExporter配置bean

③、接收连接的类WebSocketServer——加上@ServerEndpoint注解用来配置ws访问的地址信息,并实现onOpen、onClose、onMessage、onError等方法;

这里的CopyOnWriteArraySet<WebSocketServer>

concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。这里不管用户开了几个页面来与websocket接口建立连接,存进set的都是不同的session,互相不会影响。

另外看过很多博客说加了@ServerEndpoint加了注解后,同时连接多个页面的时候有冲突,,靠加注解@Scope("prototype") 单例变多例来解决,这样不一定能解决问题,我建议大家有时间还是先分析自己的程序。像set<对象>的时候就不会出现这种情况(毕竟每次存进set中的对象是不同的)。

④、基类——包括返回给前端的各种数据;

⑤、service层——写三个图对应的业务逻辑代码;

⑥、TimeTask——实现定时任务,浏览器与服务器建立连接后,后端定时向前端sendMessage。

2、WebSocket通过url鉴权token (postman可测试成功)

Spring封装

① pom文件——注入websocket依赖;

② config配置类——通过向 WebSocketHandlerRegistry 设置不同参数来进行配置。其中 addHandler 方法添加我们上面的写的 ws 的 handler 处理类,第二个参数是你暴露出的 ws 路径。addInterceptors 添加我们写的握手过滤器。setAllowedOrigins("*") 这个是关闭跨域校验,方便本地调试,线上推荐打开。

③ interceptor拦截器——HandshakeInterceptor 接口来定义握手拦截器,这里是建立握手时的事件,分为握手前与握手后

④ Manager业务类——这里简单通过 ConcurrentHashMap 来实现了一个 session 池,用来保存已经登录的 web socket 的 session。服务端发送消息给客户端必须要通过这个 session。

这个地方要注意的!!!一个用户token下 多个页面访问同一个websocket的情况

这里的map应该存的是token鉴权 Map<token,session>,若是同一个用户同时连接多个websocket接口 ,那么每个接口的token和session都会进入这个map,又因为同一个用户的token是相同的,那么map的hash值相同的情况下,只有最后一组接口才能进入map ,也就是说只有最后一个websocket接口能成功连接。

这里的解决办法是①前端改成一个大的接口包含所有数据结果,然后监听返回给多个小接口,测试可行;②后端将map中的session,改为List<session>。我看过很多博客说加注解@Scope("prototype") 单例变多例的情况,亲测没用。

主要是interceptor程序中,握手前在获取token的时候从parameter获取,postman的测试结果也在下图

// 通过URL获得请求参数
ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
Object token = serverHttpRequest.getServletRequest().getParameter("token");

众所周知,放参数在url上,多少沾点不安全,比如 xss csrf 攻击。

xss:用户通过各种方式将恶意代码注入到其他用户的页面中。就可以通过脚本获取信息,发起请求,之类的操作。

csrf:跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。csrf并不能够拿到用户的任何信息,它只是欺骗用户浏览器,让其以用户的名义进行操作。

3、WebSocket通过 自定义协议 "sec-websocket-protocol"鉴权

大部分程序还是基于第二步 Spring 封装

主要是interceptor程序中,握手前,采用自定义协议header获得token

String token = serverHttpRequest.getServletRequest().getHeader("Sec-WebSocket-Protocol");

重点来了!!!这里写完handler类中的建立连接方法 afterConnectionEstablished 确实会走,这里给人造成一种连接成功的假象,其实会立马跳到断开连接方法 afterConnectionClosed,并告诉你failed。解决办法来了!!!

在服务端接受方式(WebSocketInterceptor中 beforeHandshake方法中)加上这行代码

serverHttpResponse.getServletResponse().setHeader("Sec-WebSocket-Protocol", authorization);

public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {
        ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
        ServletServerHttpResponse serverHttpResponse = (ServletServerHttpResponse) response;
        String authorization = serverHttpRequest.getServletRequest().getHeader("Sec-WebSocket-Protocol");
        //这里对获取到的 authorization 授权码进行业务校验如 jwt 校验
        //...
        //在后端握手时设置一下请求头(Sec-WebSocket-Protocol),前端发来什么授权值,这里就设置什么值,不设置会报错导致建立连接成功后立即被关闭
        serverHttpResponse.getServletResponse().setHeader("Sec-WebSocket-Protocol", authorization);
        log.info("start shaking hands->>>");
        return true;
    }

浏览器测试的失败和成功页面。

绝了!!!

4、解决困扰我很久的问题

1:建立连接成功后立即被关闭;

2:一个用户token下 多个页面访问同一个websocket不成功的