SpringBoot 集成WebSocket
发布日期:2021-05-04 20:40:54 浏览次数:27 分类:精选文章

本文共 8907 字,大约阅读时间需要 29 分钟。

什么是WebSocket

WebSocket 是一种网络通信协议,很多高级功能都需要它。

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

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

Maven 依赖

org.springframework.boot
spring-boot-starter-websocket

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//@EnableWebSocketMessageBrokerpublic class WebSocketConfig {       @Bean    public 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}")@Componentpublic class WebSocketServer {       /**     * concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。     */    private static CopyOnWriteArraySet
webSocketSet = new CopyOnWriteArraySet<>(); /** * 与客户端的连接会话,需要通过它来给客户端发送数据 */ private Session session; /** * 接收识别码 */ private String code = ""; /** * 连接建立成功调用的方法 */ @OnOpen public 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()); } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { webSocketSet.remove(this); log.info("关闭WebSocket连接,code:" + this.code+",当前连接数:"+webSocketSet.size()); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息 */ @OnMessage public void onMessage(String message, Session session) { log.info("收到来[" + code + "]的信息:" + message); } @OnError public 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(); } } } @Override public 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); } @Override public int hashCode() { return Objects.hash(session, code); }}

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

#前端Vue代码

关于权限认证

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@Slf4jpublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter {       @Autowired    private JwtTokenUtil jwtTokenUtil;    @Value("${jwt.tokenHeader}")    private String tokenHeader;    @Value("${jwt.tokenHead}")    private String tokenHead;    @Autowired    private MyUserDetailsService userDetailsService;    @Override    protected 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 {       @Autowired    UserService userService;    @Autowired    WebSocketServer 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();    }}

效果

在这里插入图片描述

在这里插入图片描述

上一篇:kotlin_const关键字的使用规则
下一篇:三行代码生成验证码并转换成base64

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2025年03月26日 16时00分40秒