笔记
广播式
STS工具新建spring boot项目
使用Thymeleaf和Websocket依赖
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wisely</groupId>
<artifactId>ch7_6</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ch7_6_Websocket</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置WebSocket
需要在配置类上使用@EnableWebSocketMessageBroker开启WebSocket支持
package com.wisely.ch7_6;
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
/*
* 通过@EnableWebSocketMessageBroker注解开启试用STOMP协议来传输基于代理(message broker)的消息,这时控制器支持使用@MessageMapping
* 就像使用@RequestMapping一样
*
*/
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
/**
* 将"/endpointWisely"路径注册为STOMP端点,这个路径与发送和接收消息的目的路径有所不同
* 这是一个端点,客户端在订阅或发布消息到目的地址前,要连接该端点,
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {//注册STOMP协议的节点(endpoint),并映射的指定的URL
registry.addEndpoint("/endpointWisely").withSockJS();//注册一个STOMP的endpoint,并指定使用SockJS协议
//registry.addEndpoint("/hello").setAllowedOrigins("*").withSokJS();
//这个和客户端创建连接时的url有关,其中setAllowedOrigins()方法表示允许连接的域名,withSockJS()方法表示支持以SockJS方式连接服务器。
}
/**
* 配置了一个简单的消息代理,如果不重载,默认情况下回自动配置一个简单的内存消息代理,用来处理以"/topic"为前缀的消息。
* 这里重载configureMessageBroker()方法,
* 消息代理将会处理前缀为"/topic"的消息。
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {//配置消息代理(Message Broker)
registry.enableSimpleBroker("/topic");//广播式应配置一个/topic消息代理
}
/* PS
* registry.enableSimpleBroker("/topic", "/user");这句话表示在topic和user这两个域上可以向客户端发消息。
* registry.setUserDestinationPrefix("/user");这句话表示给指定用户发送一对一的主题前缀是"/user"。
* registry.setApplicationDestinationPrefixes("/app");这句话表示客户单向服务器端发送时的主题上面需要加"/app"作为前缀。
*/
}
package com.wisely.ch7_6;
/*
* 浏览器向服务端发送的消息用此类接收
*
*/
public class WiselyMessage {
private String name;
public String getName(){
return name;
}
}
package com.wisely.ch7_6;
/*
* 服务端向浏览器发送的此类的消息
*
*/
public class WiselyResponse {
private String responseMessage;
public WiselyResponse(String responseMessage){
this.responseMessage = responseMessage;
}
public String getResponseMessage() {
return responseMessage;
}
}
控制器WsController:控制器WsController:
package com.wisely.ch7_6;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class WsController {
@MessageMapping("/welcome")//当浏览器向服务端发送请求时,通过@MessageMapping映射/welcome这个地址,类似于@RequestMapping
@SendTo("/topic/getResponse")//当服务端有消息时,监听了/topic/getResponse的客户端会接收消息
public WiselyResponse say(WiselyMessage message) throws Exception{
Thread.sleep(3000);
return new WiselyResponse("Welcome," + message.getName() + "!");
}
}
前端页面ws.html:
<!--spring boot: 将stomp.min.js(STOMP协议的客户端脚本)、sockjs.min.js(SockJS的客户端脚本)以及jQuery放置在src/main/resources/static下 -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"> <!-- 页面使用Thymeleaf模板引擎 -->
<head>
<meta charset="UTF-8" />
<title>Spring Boot+WebSocket+广播式</title>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">貌似你的浏览器不支持websocket</h2></noscript>
<div>
<div>
<button id="connect" onclick="connect();">连接</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>
</div>
<div id="conversationDiv">
<label>输入你的名字</label><input type="text" id="name" />
<button id="sendName" onclick="sendName();">发送</button>
<p id="response"></p>
</div>
</div>
<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery.js}"></script>
<script type="text/javascript">
var stompClient = null;
function setConnected(connected) {
document.getElementById('connect').disabled = connected; //connected为false时则可用
document.getElementById('disconnect').disabled = !connected; //connected为false时则不可用
document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden'; //设置conversationDiv是否可见
$('#response').html();
}
function connect() {
var socket = new SockJS('/endpointWisely'); //连接SockJS的endpoint名称为"/endpointWisely"
stompClient = Stomp.over(socket);//使用STOMP自协议的WebSocket客户端
stompClient.connect({}, function(frame) { //连接WebSocket服务端
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/getResponse', function(respnose){
//通过stompClient.subscribe订阅/topic/getResponse目标(destination)发送的消息,这个是在控制器的@SendTo中定义的
showResponse(JSON.parse(respnose.body).responseMessage);//页面显示接收的消息
});
});
}
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
var name = $('#name').val();
stompClient.send("/welcome", {}, JSON.stringify({ 'name': name }));
//通过stompClient.send 向/welcome目标(destination)发送消息,这个实在控制器的@MessageMapping中定义的
} function showResponse(message) { var response = $("#response"); response.html(message); }</script></body></html>
package com.wisely.ch7_6;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/ws").setViewName("/ws");
}
}
点对点式:
添加Spring Security的依赖:
向pom.xml添加
向pom.xml添加
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>1.4.1.RELEASE</version>
</dependency>
新建 WebSecurityConfig类用于配置Spring Security
package com.wisely.ch7_6;
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;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
/*
* 在内存中配置两个用户wyf和wisely,密码与用户名一致,角色为USER
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("wyf").password("wyf").roles("USER")
.and()
.withUser("wisely").password("wisely").roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/login").permitAll()//设置Spring Security对"/"和"login"不拦截
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")//设置Spring Security的登陆页面访问的路径为/login
.defaultSuccessUrl("/chat")//登陆成功后转向/chat路径
.permitAll()
.and()
.logout()
.permitAll();
}
/*
* -/resources/static目录下的静态资源,Spring Security不拦截
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/static/**");
}
}
在WebSocket配置类中的
registerStompEndpoints方法中加入:
registry.addEndpoint("/endpointChat").withSockJS();
configureMessageBroker方法中:
registry.enableSimpleBroker("/topic");
改为:
registry.enableSimpleBroker("/queue","/topic");//新增一个用于点对点的消息代理/queue
向WsController添加以下代码:
@Autowired
private SimpMessagingTemplate messageingTemplate;//通过SimpMessagingTemplate向浏览器发送信息
@MessageMapping("/chat")
public void handleChat(Principal principal, String msg){
//Spring MVC中可以直接在参数中获得principal,principal中包含当前用户的信息
if(principal.getName().equals("wyf")){
//测试用,实际情况需根据需求编写这里判断若发件人是wyf,则发给wisely;若是wisely则发给wyf
messageingTemplate.convertAndSendToUser("wisely", "/queue/notifications", principal.getName() + "-send:" + msg);
//通过messageingTemplate.convertAndSendToUser向用户发送信息
//第一个参数为接收用户,第二个参数为订阅地址,第三个为消息
}else{
messageingTemplate.convertAndSendToUser("wyf", "/queue/notifications", principal.getName() + "-send:" + msg);
}
}
登陆页面login.html
<!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>
聊天页面chat.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8" />
<head>
<title>Home</title>
<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery.js}"></script>
</head>
<body>
<p>
聊天室
</p>
<form id="wiselyForm">
<textarea rows="4" cols="60" name="text"></textarea>
<input type="submit"/>
</form>
<script th:inline="javascript">
$('#wiselyForm').submit(function(e){
e.preventDefault();
var text = $('#wiselyForm').find('textarea[name="text"]').val();
sendSpittle(text);
});
var sock = new SockJS("/endpointChat"); //连接endpoint
var stomp = Stomp.over(sock);
stomp.connect('guest', 'guest', function(frame) {
stomp.subscribe("/user/queue/notifications", handleNotification);
//订阅/user/queue/notifications的信息,/user必须,使用user才会发送消息到指定的用户
//第二参数的地址和WsController中的
//messageingTemplate.convertAndSendToUser("wisely", "/queue/notifications", principal.getName() + "-send:" + msg);
//第二参数地址一致,相当于标志符
});
function handleNotification(message) {
$('#output').append("<b>Received: " + message.body + "</b><br/>")
}
function sendSpittle(text) {
stomp.send("/chat", {}, text);
}
$('#stop').click(function() {sock.close()});
</script>
<div id="output"></div>
</body>
</html>
WebMvcConfig添加路径映射
registry.addViewController("/login").setViewName("/login");
registry.addViewController("/chat").setViewName("/chat");