SpringBoot 集成WebSocket

什么是WebSocket

WebSocket 是一种网络通信协议,很多高级功能都需要它。
我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?
因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
如果我们想要服务器给客户端发信息,只能由客户端建立长连接这种消耗性能的操作。
WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

详细的WebSocket可以参考阮一峰的博客 :http://www.ruanyifeng.com/blog/2017/05/websocket.html

Maven 依赖

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

SpringBoot配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** Created with IntelliJ IDEA.** @Auther: zlf* @Date: 2021/04/30/16:58* @Description:*/
@Configuration
//@EnableWebSocketMessageBroker
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}}

WebSokect 通信

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;/*** Created with IntelliJ IDEA.** @Auther: zlf* @Date: 2021/04/30/17:48* @Description:*/
@Slf4j
@ServerEndpoint("/webSocket/{code}")
@Component
public class WebSocketServer {/*** concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。*/private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();/*** 与客户端的连接会话,需要通过它来给客户端发送数据*/private Session session;/*** 接收识别码*/private String code = "";/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("code") String code) {this.session = session;//如果存在就先删除一个,防止重复推送消息,实际这里实现了set,不删除问题也不大webSocketSet.removeIf(webSocket -> webSocket.code.equals(code));webSocketSet.add(this);this.code = code;log.info("建立WebSocket连接,code:" + code+",当前连接数:"+webSocketSet.size());}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {webSocketSet.remove(this);log.info("关闭WebSocket连接,code:" + this.code+",当前连接数:"+webSocketSet.size());}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session session) {log.info("收到来[" + code + "]的信息:" + message);}@OnErrorpublic void onError(Session session, Throwable error) {log.error("websocket发生错误");error.printStackTrace();}/*** 实现服务器主动推送*/private void sendMessage(String message) throws IOException {this.session.getBasicRemote().sendText(message);}/*** 群发自定义消息*/public void sendAll(String message) {log.info("推送消息到" + code + ",推送内容:" + message);for (WebSocketServer item : webSocketSet) {try {item.sendMessage(message);} catch (IOException e) {e.printStackTrace();}}}/*** 定点推送*/public void sendTo(String message, @PathParam("code") String code) {for (WebSocketServer item : webSocketSet) {try {if (item.code.equals(code)) {log.info("推送消息到[" + code + "],推送内容:" + message);item.sendMessage(message);}} catch (IOException e) {e.printStackTrace();}}}@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}WebSocketServer that = (WebSocketServer) o;return Objects.equals(session, that.session) &&Objects.equals(code, that.code);}@Overridepublic int hashCode() {return Objects.hash(session, code);}
}

这样前端发起相应的请求就可以建立双向的通信。

#前端Vue代码

<template><div>websocket</div>
</template><script>
export default {data(){return{user : JSON.parse(localStorage.getItem("user"))?JSON.parse(localStorage.getItem("user")):null}},created(){this.initWebSocket();},methods: {initWebSocket(){ //初始化weosocket// 建立连接  在请求中带上token 由SpringSecurity进行权限认证const wsuri = "ws://localhost:8001/webSocket/"+this.user.id+'?Authorization='+localStorage.getItem("token");this.websock = new WebSocket(wsuri);this.websock.onmessage = this.websocketonmessage;this.websock.onopen = this.websocketonopen;this.websock.onerror = this.websocketonerror;this.websock.onclose = this.websocketclose;},websocketonopen(){ //连接建立之后执行send方法发送数据let actions = {"test":"12345"};this.websocketsend(JSON.stringify(actions));},websocketonerror(){//连接建立失败重连console.log("----连接失败,,重连")this.initWebSocket();},websocketonmessage(e){ //数据接收//const redata = JSON.parse(e.data);console.log(e.data);},websocketsend(Data){//数据发送this.websock.send(Data);},websocketclose(e){  //关闭console.log('断开连接',e);},},
}
</script><style></style>

关于权限认证

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.youshe.mcp.utils.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** @Description: JWT登录授权过滤器* @Author: zlf* @Date: 2021/3/30*/
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate JwtTokenUtil jwtTokenUtil;@Value("${jwt.tokenHeader}")private String tokenHeader;@Value("${jwt.tokenHead}")private String tokenHead;@Autowiredprivate MyUserDetailsService userDetailsService;@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws ServletException, IOException {String authHeader = request.getHeader(this.tokenHeader);if(StringUtils.isBlank(authHeader)){// 由于webSocket 没有设置token在请求头里,而在url中// websocket连接时,令牌放在url参数上,authHeader = request.getParameter(this.tokenHeader);}if (authHeader != null && authHeader.startsWith(this.tokenHead)) {String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer "String username = jwtTokenUtil.getUserNameFromToken(authToken);//log.info("checking username:{}", username);if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {UserDetails userDetails = userDetailsService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(authToken, userDetails)) {UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, new BCryptPasswordEncoder().encode(userDetails.getPassword()), userDetails.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));//log.info("authenticated user:{}", username);SecurityContextHolder.getContext().setAuthentication(authentication);}}}chain.doFilter(request, response);}}

测试

import com.youshe.commonutils.vo.ResultVo;
import com.youshe.mcp.entity.User;
import com.youshe.mcp.service.UserService;
import com.youshe.mcp.service.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import sun.plugin.liveconnect.SecurityContextHelper;import javax.xml.transform.Result;
import java.security.Security;/*** Created with IntelliJ IDEA.** @Auther: zlf* @Date: 2021/04/30/18:03* @Description:*/
@RestController
@RequestMapping("/test")
public class testController {@AutowiredUserService userService;@AutowiredWebSocketServer webSocketServer;// 向请求的用户 推送消息@GetMapping("test")public ResultVo test(){UserDetails userDetails = (UserDetails) org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication().getPrincipal();String username = userDetails.getUsername();User user = userService.getUserByName(username);webSocketServer.sendTo("向客户端推送实时消息",user.getId());return ResultVo.ok();}
}

效果

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大学电路题目怎么搜_长沙理工大学2020真题浅析

长沙理工大学2020年821电路&#xff0c;整体感觉难度还行&#xff0c;和邱版《电路》课后题的平均水平相当。整张卷子由9道填空题和七道大题目构成&#xff0c;填空一题5分&#xff0c;大题一题15分。先说说填空&#xff0c;1是一个等效电阻&#xff0c;当然上来先观察下平衡电…

CentOS 7 搭建RAP2r Api文档管理系统

1&#xff0c;系统环境 a&#xff0c;操作系统 CentOS Linux release 7.6.1810 (Core) 64位 2&#xff0c;安装npm环境&#xff1a; # Node 官网已经把 linux 下载版本更改为已编译好的版本了&#xff0c;我们可以直接下载解压后使用&#xff1a; wget https://nodejs.org…

转:VMware、微软等四种主要的网络IO虚拟化模型

本文主要为大家简要介绍VMware、Redhat、Citrix、Microsoft主要虚拟化厂商使用的4种主要的虚拟化IO模型(emulation、para-virtualization、pass-through、SR-IOV)。 本文主要为大家穿针引线&#xff0c;信息量比较大&#xff0c;组织排版有限&#xff0c;看官们将就点看着。 网…

CentOS7安装go开发环境

1&#xff0c;系统环境 操作系统 CentOS Linux release 7.6.1810 (Core) 64位 执行以下命令&#xff1a; wget https://dl.google.com/go/go1.12.5.linux-amd64.tar.gz tar -xzvf go1.12.5.linux-amd64.tar.gz -C /usr/local/ mkdir -p /home/gopath cat >> /etc/p…

非递归遍历二叉树

2019独角兽企业重金招聘Python工程师标准>>> http://hi.baidu.com/lcplj123/item/7875233769fd5522b2c0c582 转载于:https://my.oschina.net/u/939893/blog/126138

Vue.js使用矢量图

安装依赖 npm i svg-sprite-loader --save目录结构 创建 svg-icon 组件 <template><svg :class"svgClass" aria-hidden"true"><use :xlink:href"iconName"/></svg> </template><script> export default {…

aix oracle监听配置_Oracel:ORA-12518:监听程序无法分发客户机连接

一、【问题描述】最近&#xff0c;在系统高峰期的时候&#xff0c;会提示如上的错误&#xff0c;致使无法连接到服务器上的数据库。二、【分析过程】1、首先判断是否由于监听配置不正确的原因导致?系统在正常情况下都可以正常的使用&#xff0c;检查监听配置&#xff0c;完全正…

PowerShell巡检主机获取CPU占用、内存使用、硬盘情况的脚本

Windows底下用PowerShell写的获取CPU核数、占用率&#xff1b;内存可用内存大小&#xff08;GB&#xff09;、使用率&#xff1b;硬盘总空余大小&#xff08;GB&#xff09;&#xff0c;使用率 # 获取硬盘空余空间 function get_disk_free(){ $disk Get-WmiObject -Class win…

4 命名规则_赛普拉斯(Cypress)存储器芯片命名规则

1&#xff0c;前言 赛普拉斯(Cypress)公司是一家知名的电子芯片制造商。赛普拉斯在纽约股票交易所上市&#xff0c;在数据通信、消费类电子等广泛领域均提供芯片解决方案。 2020年4月16日赛普拉斯(Cypress)和英飞凌(infineon)同时对外发文宣布&#xff1a;infineon英飞凌已经完…

数据数据库学通MongoDB——第一天 基础入门

在本文中,我们主要介绍数据数据库的内容,自我感觉有个不错的建议和大家分享下 关于mongodb的利益&#xff0c;长处之类的这里就不说了&#xff0c;一唯要讲的一点就是mongodb中有三元素&#xff1a;数据库&#xff0c;合集&#xff0c;文档&#xff0c;其中“合集” 就是对应关…

win10雷电3接口驱动_技嘉推出B550 主板首发雷电3接口:40Gbps速率、Intel主控

下周AMD针对主流市场的B550芯片组就要上市了&#xff0c;千元级主板上也有PCIe 4.0了。技嘉今天又宣布了一款新型号主板——B550 Vison D&#xff0c;它竟然支持Intel独家的Thunderbolt 3&#xff08;俗称雷电3&#xff09;接口&#xff0c;这还是B550中首款&#xff0c;40Gbps…

eclipse--eclipse(JavaEE版本)部署Tomcat工程(转)

介绍如何在eclipse&#xff08;JavaEE版本&#xff09;中部署Tomcat工程&#xff0c; 转自“http://www.cnblogs.com/chenjunbiao/archive/2011/12/09/2281702.html” “http://www.iteye.com/topic/825394” Eclipse下Tomcat常用设置 1&#xff0c;Eclipse建立Tomcat服务 1.1 …

python工程师需要考什么证_考垃圾处理清运工程师证哪里颁发的今年的考试时间即将告知...

考垃圾处理清运工程师证哪里颁发的今年的考试时间即将告知二、中级会计师(会计师)资格考试。三、高级会计师资格考试。四、中级会计师资格考试。五、注册税务师职称。六、注册公司登记。七、注册会计师、法人和其他组织的税务师资格考试。八、注册税务师职称。而在报名的时候不…

mysql集群搭建(使用docker 一主一从)

mysql集群搭建 my.cnf 配置文件配置 在 /etc/mysql/my.cnf 中 &#xff08;拿一个举例&#xff09; &#xff08;docker中需要先进入开启的容器&#xff0c;docker exec -it 容器名称 /bin/bash&#xff09; [mysqld] #启用二进制日志 log-binmysql-bin #服务器唯一ID&…

华为主题包hwt下载_华为主题 | 黑白人物

黑白人物1前言每周更新一次&#xff0c;没办法量产很抱歉但每一个主题都很精致这次的主题新增了QQ美化&#xff0c;锤子便签还有白肚皮美化不会太花里胡哨&#xff0c;放心不要喷我&#xff0c;所用壁纸皆是在堆糖里寻找喜欢的宝宝们&#xff0c;可以帮忙点一下再看或者关注不迷…

filebeat + logstash 发送日志至kafka 入门

filebeat 官方文档 配置文件 filebeat.yml filebeat.inputs:# Each - is an input. Most options can be set at the input level, so # you can use different inputs for various configurations. # Below are the input specific configurations.- type: log# Change to …

上机环境是什么意思_380元入手RX580满血显卡,跑分17万,还要什么自行车

今年显卡的行情都要比去年上涨一些&#xff0c;特别是刚过完年那一段时间&#xff0c;价格上涨的尤其的快&#xff0c;一张显卡上涨几十块&#xff0c;当时也是不敢入手 &#xff0c;等到了现在行情总算要好一些了&#xff0c;价格也适当的在往下走&#xff0c;最后入手了一款性…

docker-conpose 入门

docker-compose 安装 官方地址github Linux 下安装 sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose要安装其他版本的 Compose&#xff0c;请替换 1.29.1 …

史上最全 yum 入门使用教程和常见错误解决办法

介绍 众所周知&#xff0c;Redhat和Fedora的软件安装命令是rpm。需要手动寻找安装该软件所需要的一系列依赖关系&#xff0c;yum的诞生很好解决了以上的问题&#xff0c;下面有几个实用的yum小技巧和大家分享。 rpm与yum常用命令集合 $ rpm -qa | grep jenkins …

lru调度算法例题_嵌入式必会!C语言最常用的贪心算法就这么被攻略了

01基本概念贪心算法是指在对问题求解时&#xff0c;总是做出在当前看来是最好的选择。也就是说&#xff0c;不从整体最优上加以考虑&#xff0c;只做出在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解&#xff0c;关键是贪心策略的选择&#xff0c;选择的…