在這之前,已經(jīng)做過一個Spring Boot使用WebSocket的廣播式運用:
Spring Boot(五):webSocket組件的入門簡單使用
現(xiàn)在榔昔,我們再來進(jìn)一步了解webSocket;
下面我們使用webSocket組件創(chuàng)建一對一的聊天室:
一.首先先創(chuàng)建前端頁面嘹朗,代碼如下圖所示:
1.login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登錄頁面</title>
</head>
<body>
<h2>自定義登錄頁面</h2>
<form action="/user/login" method="post">
<table>
<tr>
<td>用戶名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密碼:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><button type="submit">登錄</button></td>
</tr>
</table>
</form>
</body>
</html>
2.chat.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8" />
<head>
<title>Home</title>
<script src="../js/jquery-2.1.1.min.js"></script>
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.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("/stomp"); //1連接SockJS的endpoint是“stomp”屹培,與后臺代碼中注冊的endpoint要一樣惫谤。-
var stomp = Stomp.over(sock);//2<!--(2)創(chuàng)建STOMP協(xié)議的webSocket客戶端溜歪。-->
stomp.connect('guest', 'guest', function(frame) {//3<!--(3)連接webSocket的服務(wù)端。-->
stomp.subscribe("/user/queue/notifications", handleNotification);////4 通過stompClient.subscribe()訂閱服務(wù)器的目標(biāo)是'/queue/notifications'發(fā)送過來的地址蝴猪,
});
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>
二.在spring boot中創(chuàng)建spring Security自阱,代碼如下圖所示:
1.設(shè)置主頁IndexPageConfig沛豌;
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.RedirectView;
/**
* @author AxeLai
* @date 2019-04-29 09:31
*/
@Controller
public class IndexPageConfig {
@RequestMapping("/")
public RedirectView ws() {
return new RedirectView("/pages/chat.html");
}
}
2.SecurityConfig類:
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author AxeLai
* @date 2019-04-30 15:15
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Bean
public PasswordEncoder passwordEncoder(){
//當(dāng)然加派,如果你有自己的加密方法,這個方法就寫自己的加密方法好了
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置需要登入驗證
// http.formLogin() // 定義當(dāng)需要用戶登錄時候竹勉,轉(zhuǎn)到的登錄頁面次乓。
// .and()
// .authorizeRequests() // 定義哪些URL需要被保護(hù)孽水、哪些不需要被保護(hù)
// .anyRequest() // 任何請求,登錄后可以訪問
// .authenticated();
//配置需要登入驗證的自定義配置
http.formLogin() // 定義當(dāng)需要用戶登錄時候,轉(zhuǎn)到的登錄頁面丧慈。
.loginPage("/login.html") // 設(shè)置登錄頁面
.loginProcessingUrl("/user/login") // 自定義的登錄接口
.and()
.authorizeRequests() // 定義哪些URL需要被保護(hù)主卫、哪些不需要被保護(hù)
.antMatchers("/login.html").permitAll() // 設(shè)置所有人都可以訪問登錄頁面
.anyRequest() // 任何請求,登錄后可以訪問
.authenticated()
.and()
.csrf().disable(); // 關(guān)閉csrf防護(hù)
//配置不需要登入驗證
// http.authorizeRequests()
// .anyRequest()
// .permitAll()
// .and()
// .logout()
// .permitAll();
}
//4
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user1").password(passwordEncoder.encode("123")).roles("USER")//這里兩個是分別賬號和密碼(賬號:user1密碼:123)
.and()
.withUser("user2").password(passwordEncoder.encode("123")).roles("USER");//這里兩個是分別賬號和密碼(賬號:user2密碼:123)
}
//5忽略靜態(tài)資源的攔截
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/static/**");
}
}
3.在原來的WsController類中增加聊天的方法handleChat()簇搅,代碼如下圖所示:
package com.example.demo.controller;
import com.example.demo.model.WiselyMessage;
import com.example.demo.model.WiselyResponse;
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;
/**
* @author AxeLai
* @date 2019-04-29 09:08
*/
@Controller
public class WsController {
@MessageMapping("/welcome")//1@MessageMapping和@RequestMapping功能類似瘩将,用于設(shè)置URL映射地址,瀏覽器向服務(wù)器發(fā)起請求姿现,需要通過該地址。
@SendTo("/topic/getResponse")//2如果服務(wù)器接受到了消息异旧,就會對訂閱了@SendTo括號中的地址傳送消息
public WiselyResponse say(WiselyMessage message) throws Exception {
return new WiselyResponse("你好, " + message.getName() + "!");
}
@Autowired
private SimpMessagingTemplate messagingTemplate;//1 SimpMessagingTemplate用于向瀏覽器發(fā)送信息吮蛹。
@MessageMapping("/chat")
public void handleChat(Principal principal, String msg) { //2在spring mvc中,principal包含了登錄用戶的信息潮针,在這里我們直接用每篷。
if (principal.getName().equals("user1")) {//3這里是一段寫死的代碼,如果登錄的用戶是dzz,那就將消息發(fā)送給zbb带兜,大家根據(jù)自己的需求進(jìn)行修改吨灭。通過convertAndSendToUser()方法進(jìn)行發(fā)送刑巧,第一個參數(shù)是信息接收的目標(biāo),第二個參數(shù)是要發(fā)送的地址吠冤,第三個參數(shù)是要發(fā)送的信息恭理。
messagingTemplate.convertAndSendToUser("user2",
"/queue/notifications", principal.getName() + "-send:"
+ msg);
} else {
messagingTemplate.convertAndSendToUser("user2",
"/queue/notifications", principal.getName() + "-send:"
+ msg);
}
}
}
4.在原來的WebSocketConfig類中再創(chuàng)建一個節(jié)點(endpoint)和代理(MessageBroker),代碼如下圖所示:
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Value;
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;
import org.springframework.web.socket.config.annotation.StompWebSocketEndpointRegistration;
/**
* Created by AxeLai on 2019/04/29 0011.
*/
@Configuration
@EnableWebSocketMessageBroker//(1),@EnableWebSocketMessageBroker注解用于開啟使用STOMP協(xié)議來傳輸基于代理(MessageBroker)的消息涯保,這時候控制器(controller)開始支持@MessageMapping,就像是使用@requestMapping一樣夕春。
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/stomp").withSockJS(); //(2),注冊一個Stomp的節(jié)點(endpoint),并指定使用SockJS協(xié)議专挪。
//stomp就是websocket的端點,客戶端需要注冊這個端點進(jìn)行鏈接速侈,withSockJS允許客戶端利用sockjs進(jìn)行瀏覽器兼容性處理
}
// @Override
// public void configureMessageBroker(MessageBrokerRegistry registry) {
// registry.enableSimpleBroker("/topic");//(3),配置消息代理(MessageBroker)倚搬。
// //設(shè)置服務(wù)器廣播消息的基礎(chǔ)路徑
//// registry.setApplicationDestinationPrefixes("/app");//設(shè)置客戶端訂閱消息的基礎(chǔ)路徑
// }
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//設(shè)置一對一聊天室的基礎(chǔ)路徑
registry.enableSimpleBroker("/queue","/topic");
}
}
三.效果演示
啟動項目潭枣,分別在兩個瀏覽器進(jìn)行訪問,一個用賬號user1一個用賬號user2盆犁,密碼都是123:
登入并發(fā)送消息谐岁,結(jié)果如下:
圖片.png
另一個賬號的界面就可以看到:
圖片.png
圖片.png
最后附上項目結(jié)構(gòu):
圖片.png
項目還是放在原來的github目錄上窜司;