SpringBoot2实战训练demo3之WebSocket点对点发送消息
在WebSocket广播式的基础上修改即可
第一步:我们在pom.xml配置文件中增加Spring Security
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
第二步:创建Spring Security的简单配置类
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.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Spring Security的简单配置
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
//设置Spring Security对/和/“login”路径不拦截
.antMatchers("/","/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
//设置Spring Security的登录页面访问的路径为/login
.loginPage("/login")
//登陆成功后转向/chat路径
.defaultSuccessUrl("/chat")
.permitAll()
.and()
.logout()
.permitAll();
}
//在内存中分别配置两个用户aaa和bbb,密码和用户名一致,角色是USER
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("aaa").password(passwordEncoder().encode("aaa")).roles("USER")
.and()
.withUser("bbb").password(passwordEncoder().encode("bbb")).roles("USER");
}
//Spring Security不拦截"/resources/static/"目录下的静态资源
@Override
public void configure(WebSecurity web) throws Exception{
web.ignoring().antMatchers("/resources/static/**");
}
//密码处理
private static PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
第三步:配置WebSocket,注册一个名为/endpointChat的endpoint,点对点式应增加一个/queue消息代理
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
//通过@EnableWebSocketMessageBroker注解开启使用STOMP协议来传输基于代理(message broker)的消息,
// 这时控制器支持使用@MessageMapping,就像使用@RequestMapping一样
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
//注册STOMP协议的节点(endpoint),并映射的指定的URL
@Override
public void registerStompEndpoints(StompEndpointRegistry registry){
//注册一个STOMP的endpoint,并指定使用SocketJS协议
registry.addEndpoint("/endpointDemo").withSockJS();
//注册一个名为/endpointChat的endpoint
registry.addEndpoint("/endpointChat").withSockJS();
}
//配置消息代理(Message Broker)
@Override
public void configureMessageBroker(MessageBrokerRegistry registry){
//广播式应配置一个/topic消息代理
//点对点式应增加一个/queue消息代理
registry.enableSimpleBroker("/queue","/topic");
}
}
第四步:控制器WsController更改
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import java.security.Principal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* 演示控制器
*/
@Controller
public class WsController {
//通过SimpMessagingTemplate向浏览器发送消息
@Autowired
private SimpMessagingTemplate messagingTemplate;
//当浏览器向服务端发送请求时,通过@MessageMapping映射/welcome这个地址,类似于@RequestMapping
@MessageMapping("/welcome")
//当服务端有消息时,会对订阅了@SendTo中的路径的浏览器发送消息
@SendTo("/topic/getResponse")
public DemoResponse say(DemoMessage message) throws Exception {
Thread.sleep(3000);
return new DemoResponse("欢迎你:" + message.getName() + "!");
}
/**
* 点对点
*/
//在Spring MVC中,可以直接在参数中获得principal,pinciple中包含当前用户的信息
@MessageMapping("/chat")
public void handleChat(Principal principal, String msg) {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//这里是一段硬编码,如果发送人是aaa,则发送给bbb;如果发送人是bbb,则发送给aaa,
if (principal.getName().equals("aaa")) {
messagingTemplate.convertAndSendToUser("bbb",
"/queue/notifications", principal.getName() + "-发来消息:" + msg + "\t" +sdf.format(date));
} //通过messagingTemplate.convertAndSendToUser向用户发送消息,第一个参数是接收消息的用户,
// 第二个是浏览器订阅的地址,第三个是消息本身
else {
messagingTemplate.convertAndSendToUser("aaa",
"/queue/notifications", principal.getName() + "-发来消息:" + msg + " " +sdf.format(date));
}
}
}
第五步:创建登录页面
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<meta charset="UTF-8">
<head>
<title>登录页面</title>
</head>
<body>
<div th:if="${param.error}">
无效的账号和密码
</div>
<div th:if="${param.logout}">
你已注销
</div>
<form th:action="@{/login}" method="post">
<div>
<label>账号:<input type="text" name="username"/></label>
</div>
<div>
<label>密码:<input type="password" name="password"/></label>
</div>
<div>
<input type="submit" value="登录"/>
</div>
</form>
</body>
</html>
第六步:创建简单聊天页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>聊天室</title>
<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery.min.js}"></script>
</head>
<body>
<p>
聊天室
</p>
<form id="demoFrom">
<textarea rows="4" cols="60" name="text"></textarea><br>
<input type="submit" value="发送消息"/>
</form>
<script th:inline="javascript">
$('#demoFrom').submit(function (e) {
e.preventDefault();
var text = $('#demoFrom').find('textarea[name="text"]').val();
sendSpittle(text);
});
//连接endpoint名称为“/endpointChat”的endpoint
var sock = new SockJS("/endpointChat");
var stomp = Stomp.over(sock);
stomp.connect('guest', 'guest', function (frame) {
//订阅/user/queue/notifications 发送的消息,这里与在控制器的messagingTemplate.converAndSendToUser中
//定义的订阅地址保持一致。这里的/user是必须的,使用了/user才会发送消息到指定的用户
stomp.subscribe("/user/queue/notifications", handleNotification);
});
function handleNotification(message) {
$('#output').append("<b>你的好友:" + message.body + "</b><br>")
}
function sendSpittle(text) {
//
stomp.send("/chat", {}, text);
}
$('#stop').click(function () {
sock.close()
});
</script>
<div id="output"></div>
</body>
</html>
第七步:增加页面的viewController
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* 配置viewController,为ws.html提供便捷的路径映射
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/ws").setViewName("/ws");
registry.addViewController("/login").setViewName("/login");
registry.addViewController("/chat").setViewName("/chat");
}
}
第八步:运行看效果:
两个浏览器登录不同账户

登录后给对方发送消息


浙公网安备 33010602011771号