SpringBoot+WebSocket點對點訊息通訊應用
阿新 • • 發佈:2019-01-04
1、在pom檔案中新增SpringSecurity依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2、簡單配置SpringSecurity的登入操作
package com.websocket.onetoone.config; import com.websocket.onetoone.utils.MyPasswordEncoder; 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; /** * @ClassName WebSecurityConfig * @Description TODO * @Author shanzz * @Date 2019/1/2 11:08 * @Version 1.0 **/ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/","/login").permitAll()//設定SpringSecurity對/和”/login"路徑不攔截 .anyRequest().authenticated() .and() .formLogin() .loginPage("/login")//設定SpringSecurity 的登入頁面訪問的路徑為/login .defaultSuccessUrl("/chat")//登入成功後轉向/chat路徑 .permitAll() .and() .logout() .permitAll(); } //在記憶體中分別配置兩個使用者szz和wisely,密碼和使用者名稱一致,角色名是USER @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder()) .withUser("szz").password("szz").roles("USER") .and() .withUser("wisely").password("wisely").roles("USER"); } // /resource/static/目錄下的靜態資源,SpringSecurity不攔截 @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/resources/static/**"); } }
Spring boot 2.x引用的security 依賴是 spring security 5.X版本,此版本需要提供一個PasswordEncorder的例項,否則後臺彙報錯誤:
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
建立 MyPasswordEncoder例項
package com.websocket.onetoone.utils; import org.springframework.security.crypto.password.PasswordEncoder; /** * @ClassName MyPasswordEncoder * @Description TODO * @Author shanzz * @Date 2019/1/2 13:50 * @Version 1.0 **/ public class MyPasswordEncoder implements PasswordEncoder { @Override public String encode(CharSequence charSequence) { return charSequence.toString(); } @Override public boolean matches(CharSequence charSequence, String s) { return s.equals(charSequence.toString()); } }
3、配置websocket
package com.websocket.onetoone.config; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; /** * @ClassName WebSocketConfig * @Description TODO * @Author shanzz * @Date 2019/1/2 13:08 * @Version 1.0 **/ @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/endpointWisely").withSockJS(); registry.addEndpoint("/endpointChat").withSockJS();//註冊一個名為endpointChat的endpoint } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/queue","/topic");//點對點式增加一個/queue訊息代理 } }
4、配置控制器
package com.websocket.onetoone.controller;
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;
/**
* @ClassName WebSocketController
* @Description TODO
* @Author shanzz
* @Date 2019/1/2 13:14
* @Version 1.0
**/
@Controller
public class WebSocketController {
@Autowired
private SimpMessagingTemplate messagingTemplate;//通過SimpMessagingTemplate向瀏覽器傳送訊息
@MessageMapping("/chat")
public void handleChat(Principal principal,String msg){//在SpringMVC中,可以直接在引數中獲得principal,pinciple中包含當前使用者的資訊
/**
* 這裡是一段死程式碼,如果傳送人是szz,則傳送給wisely;如果傳送人是wisely,則傳送給szz.
* 可以根據自己的業務邏輯改造此段程式碼
**/
if (principal.getName().equals("szz")){
/**
*通過messagingTemplate.convertAndSendToUser向用戶傳送訊息,
* 第一個引數是接收訊息的使用者
* 第二個是瀏覽器訂閱的地址
* 第三個是訊息本身
**/
messagingTemplate.convertAndSendToUser("wisely",
"/queue/notifications",principal.getName()+"-send:"+msg);
}else{
messagingTemplate.convertAndSendToUser("szz","/queue/notifications",principal.getName()+"-send:"+msg);
}
}
}
5、登入介面
<!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">
<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名稱為"/endpointChat"的endpoint
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) {
$('#output').append("<b>Received: " + message.body + "</b><br/>")
}
function sendSpittle(text) {
stomp.send("/chat", {}, text);//3
}
$('#stop').click(function() {sock.close()});
</script>
<div id="output"></div>
</body>
</html>
6、新增頁面解析器
package com.websocket.onetoone.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @ClassName WebMvcConfig
* @Description TODO
* @Author shanzz
* @Date 2019/1/2 13:32
* @Version 1.0
**/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("/login");
registry.addViewController("/chat").setViewName("/chat");
}
}
7、執行程式,訪問
參考書籍:JavaEE開發的顛覆者:SpringBoot實戰第七章 7.6.3