微信公众号:一个优秀的废人。如有问题,请后台留言,反正我也不会听。
前言
昨天那篇介绍了 WebSocket 实现广播,也即服务器端有消息时,将消息发送给所有连接了当前 endpoint 的浏览器。但这无法解决消息由谁发送,又由谁接收的问题。所以,今天写一篇实现一对一的聊天室。
今天这一篇建立在昨天那一篇的基础之上,为便于更好理解今天这一篇,推荐先阅读:「 」
准备工作
- Spring Boot 2.1.3 RELEASE
- Spring Security 2.1.3 RELEASE
- IDEA
- JDK8
pom 依赖
因聊天室涉及到用户相关,所以在上一篇基础上引入 Spring Security 2.1.3 RELEASE 依赖
复制代码 org.springframework.boot spring-boot-starter-security
Spring Security 的配置
虽说涉及到 Spring Security ,但鉴于篇幅有限,这里只对这个项目相关的部分进行介绍,具体的 Spring Security 教程,后面会出。
这里的 Spring Security 配置很简单,具体就是设置登录路径、设置安全资源以及在内存中创建用户和密码,密码需要注意加密,这里使用 BCrypt 加密算法在用户登录时对密码进行加密。 代码注释很详细,不多说。
package com.nasus.websocket.config;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.builders.WebSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;@Configuration// 开启Spring Security的功能@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter{ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 设置 SpringSecurity 对 / 和 "/login" 路径不拦截 .mvcMatchers("/","/login").permitAll() .anyRequest().authenticated() .and() .formLogin() // 设置 Spring Security 的登录页面访问路径为/login .loginPage("/login") // 登录成功后转向 /chat 路径 .defaultSuccessUrl("/chat") .permitAll() .and() .logout() .permitAll(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() // 在内存中分配两个用户 nasus 和 chenzy ,用户名和密码一致 // BCryptPasswordEncoder() 是 Spring security 5.0 中新增的加密方式 // 登陆时用 BCrypt 加密方式对用户密码进行处理。 .passwordEncoder(new BCryptPasswordEncoder()) .withUser("nasus") // 保证用户登录时使用 bcrypt 对密码进行处理再与内存中的密码比对 .password(new BCryptPasswordEncoder().encode("nasus")).roles("USER") .and() // 登陆时用 BCrypt 加密方式对用户密码进行处理。 .passwordEncoder(new BCryptPasswordEncoder()) .withUser("chenzy") // 保证用户登录时使用 bcrypt 对密码进行处理再与内存中的密码比对 .password(new BCryptPasswordEncoder().encode("chenzy")).roles("USER"); } @Override public void configure(WebSecurity web) throws Exception { // /resource/static 目录下的静态资源,Spring Security 不拦截 web.ignoring().antMatchers("/resource/static**"); }}复制代码
WebSocket 的配置
在上一篇的基础上另外注册一个名为 "/endpointChat" 的节点,以供用户订阅,只有订阅了该节点的用户才能接收到消息;然后,再增加一个名为 "/queue" 消息代理。
@Configuration// @EnableWebSocketMessageBroker 注解用于开启使用 STOMP 协议来传输基于代理(MessageBroker)的消息,这时候控制器(controller)// 开始支持@MessageMapping,就像是使用 @requestMapping 一样。@EnableWebSocketMessageBrokerpublic class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { //注册一个名为 /endpointNasus 的 Stomp 节点(endpoint),并指定使用 SockJS 协议。 registry.addEndpoint("/endpointNasus").withSockJS(); //注册一个名为 /endpointChat 的 Stomp 节点(endpoint),并指定使用 SockJS 协议。 registry.addEndpoint("/endpointChat").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { // 广播式配置名为 /nasus 消息代理 , 这个消息代理必须和 controller 中的 @SendTo 配置的地址前缀一样或者全匹配 // 点对点增加一个 /queue 消息代理 registry.enableSimpleBroker("/queue","/nasus/getResponse"); }}复制代码
控制器 controller
指定发送消息的格式以及模板。详情见,代码注释。
@Autowired//使用 SimpMessagingTemplate 向浏览器发送信息private SimpMessagingTemplate messagingTemplate;@MessageMapping("/chat")public void handleChat(Principal principal,String msg){ // 在 SpringMVC 中,可以直接在参数中获得 principal,principal 中包含当前用户信息 if (principal.getName().equals("nasus")){ // 硬编码,如果发送人是 nasus 则接收人是 chenzy 反之也成立。 // 通过 messageingTemplate.convertAndSendToUser 方法向用户发送信息,参数一是接收消息用户,参数二是浏览器订阅地址,参数三是消息本身 messagingTemplate.convertAndSendToUser("chenzy", "/queue/notifications",principal.getName()+"-send:" + msg); } else { messagingTemplate.convertAndSendToUser("nasus", "/queue/notifications",principal.getName()+"-send:" + msg); }}复制代码
登录页面
复制代码登陆页面 无效的账号和密码你已注销
聊天页面
复制代码Home 聊天室
页面控制器 controller
@Controllerpublic class ViewController { @GetMapping("/nasus") public String getView(){ return "nasus"; } @GetMapping("/login") public String getLoginView(){ return "login"; } @GetMapping("/chat") public String getChatView(){ return "chat"; }}复制代码
测试
预期结果应该是:两个用户登录系统,可以互相发送消息。但是同一个浏览器的用户会话的 session 是共享的,这里需要在 Chrome 浏览器再添加一个用户。
具体操作在 Chrome 的 设置-->管理用户-->添加用户:
两个用户分别访问 http://localhost:8080/login 登录系统,跳转至聊天界面:
相互发送消息:
完整代码
https://github.com/turoDog/Demo/tree/master/springboot_websocket_demo
如果觉得对你有帮助,请给个 Star 再走呗,非常感谢。
后语
如果本文对你哪怕有一丁点帮助,请帮忙点好看。你的好看是我坚持写作的动力。
另外,关注之后在发送 1024 可领取免费学习资料。
资料详情请看这篇旧文: