添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配置SpringSecurity:
package com.example.demo.part3.chapter7.Chat;
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 {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
//在内存中分别配置两个用户sasa sa和密码 ,角色是user
.withUser("sasa").password("123").roles("user")
.and()
.withUser("sa").password("123").roles("user");
}
@Override
public void configure(WebSecurity web) throws Exception {
///resources/static/ 目录下的静态资源,spring security不拦截
web.ignoring().antMatchers("/resources/static/**");
}
@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();
}
}
配置WebSocket:
package com.example.demo.part3.chapter7.Chat;
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
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//点对点式应增加一个/queue消息代理
registry.enableSimpleBroker("/queue","/topic");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry endpoint) {
//注册一个名为 endPointChat 的endpoint
endpoint.addEndpoint("/endPointChat").withSockJS();
}
}
控制器:
package com.example.demo.part3.chapter7.Chat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import java.security.Principal;
@Controller
public class ChatController {
@Autowired
//通过simpleMessagingTemplate向浏览器发送消息
private SimpMessagingTemplate messagingTemplate;
@MessageMapping("/chat")
//在springmvc中,可以直接在参数中获取principle,principle中包含当前用户的信息
public void handleChat(Principal principal,String msg){
System.out.println(1);
if(principal.getName().equals("sasa")){
//通过 convertAndSendToUser方法向用户发送信息,第一个参数是消息接收人,
//第二个是浏览器订阅地址,第三个是消息本身
System.out.println(2);
messagingTemplate.convertAndSendToUser("sa",
"/queue/notifications",principal.getName()
+"-send:"+msg);
}else{
System.out.println(3);
messagingTemplate.convertAndSendToUser("sasa",
"/queue/notifications",principal.getName()
+"-send:"+msg);
}
}
}
登录页面:
<!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" name="登录"/>
</div>
</form>
</body>
</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>
<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery-1.8.3.min.js}"></script>
</head>
<body>
<form id="chatForm">
<textarea rows="4" cols="60" name="text" id="text" />
<input type="submit"/>
</form>
<script type="text/javascript">
$("#chatForm").submit(function (e) {
e.preventDefault();
var text=$("#text").val();
sendMsg(text);
});
//连接endpoint名称为 /endPointChat 的endpoint
var sock=new SockJS("/endPointChat");
var stomp=Stomp.over(sock);
stomp.connect('guest','guest',function (frame) {
//订阅 /user/queue/notifications 发送的消息,这里与
//在控制器的messagingTemplate.convertAndSendToUser中定义的订阅地址保持一致
//这里多了 /user ,并且这个 /user是必须的,使用了 /user 才会将消息发送到指定用户
stomp.subscribe("/user/queue/notifications",handleNotification);
});
function handleNotification(message) {
console.log("msg"+message);
$("#output").append('<b>'+message.body+'</b>');
}
function sendMsg(text) {
stomp.send("/chat",{},text);
}
$("#stop").click(function () {
sock.close();
})
</script>
<div id="output"></div>
</body>
</html>
页面控制:
package com.example.demo.part3.chapter7.WebSocket;
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) {
//super.addViewControllers(registry);
registry.addViewController("/login").setViewName("/login");
registry.addViewController("/chat").setViewName("/chat");
}
}
效果展示: