不一樣上文Spring Boot系列十七 Spring Boot 集成 websocket,使用RabbitMQ作爲消息代理,本文咱們介紹經過Spring websocket實現向特定的用戶發送消息。 本文的內容以下: 1. 首先實現簡單的登陸功能,這裏向特定用戶發送消息的必要條件 2. 用戶登陸系統後,才能夠登陸websocket,並重寫MyPrincipal 3. 實現向特定用戶發送消息的功能 4. 測試javascript
TestMQCtl:控制類 提供模擬登陸,登陸成功後轉到websocket頁面html
/**
* 模擬登陸 */
@RequestMapping(value = "loginIn", method = RequestMethod.POST)
public String login(HttpServletRequest request, @RequestParam(required=true) String name, String pwd){
HttpSession httpSession = request.getSession();
// 若是登陸成功,則保存到會話中
httpSession.setAttribute("loginName", name);
return "websocket/sendtouser/ws-sendtouser-rabbitmq";
}
/**
* 轉到登陸頁面
*/
@RequestMapping(value = "login", method = RequestMethod.GET)
public String loginPage(){
// 轉到登陸頁面
return "websocket/sendtouser/login";
}
/**
* websocket頁面
* @return
*/
@RequestMapping(value="/broadcast-rabbitmq/index")
public String broadcastIndex(){
return "websocket/sendtouser/ws-sendtouser-rabbitmq";
}
複製代碼
login.jsp 簡單的form表單,將請求提到loginIn,並轉到ws-sendtouser-rabbitmq.jsp頁面java
<form action="loginIn" method="post">
用戶名:<input type="text" name="name" />
<p>
密碼:<input type="password" name="password" />
<p>
<input type="submit" value="submit" />
</form>
複製代碼
ws-sendtouser-rabbitmq.jsp 鏈接websocket並訂閱消息,這個jsp以前的文章已經介紹過了這裏不詳細描述。頁面經過向/ws/icc/websocket啓動websocket,而後訂閱/user/topic/demo消息jquery
<script type="text/javascript">
var stompClient = null;
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
$('#response').html();
}
function connect() {
// websocket的鏈接地址,此值等於WebSocketMessageBrokerConfigurer中registry.addEndpoint("/ws/icc/websocket").withSockJS()配置的地址
var socket = new SockJS('/ws/icc/websocket'); //1
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
// 客戶端訂閱消息的目的地址:此值等於BroadcastCtl中@SendTo註解的裏配置的值。
stompClient.subscribe(
'/user/topic/demo',
function(respnose){
showResponse(JSON.parse(respnose.body));
}
);
});
}
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function showResponse(message) {
var response = $("#response");
response.html(message.name + "<br\>" + response.html());
}
</script>
複製代碼
AuthHandshakeInterceptor AuthHandshakeInterceptor是HandshakeInterceptor 的子類。在websocket握手前判斷,判斷當前用戶是否已經登陸。若是未登陸,則不容許登陸websocketgit
@Component
public class AuthHandshakeInterceptor implements HandshakeInterceptor {
private static final Logger log = LoggerFactory.getLogger(AuthHandshakeInterceptor.class);
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
HttpSession httpSession = getSession(request);
String user = (String)httpSession.getAttribute("loginName");
if(StringUtils.isEmpty(user)){
log.error("未登陸系統,禁止登陸websocket!");
return false;
}
log.info("login = " + user);
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
}
// 參考 HttpSessionHandshakeInterceptor
private HttpSession getSession(ServerHttpRequest request) {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
return serverRequest.getServletRequest().getSession(false);
}
return null;
}
}
複製代碼
MyPrincipalHandshakeHandler MyPrincipalHandshakeHandler是DefaultHandshakeHandler 的子類,處理websocket請求,這裏咱們只重寫determineUser方法,生成咱們本身的Principal ,這裏咱們使用loginName標記登陸用戶,而不是默認值github
@Component
public class MyPrincipalHandshakeHandler extends DefaultHandshakeHandler {
private static final Logger log = LoggerFactory.getLogger(MyPrincipalHandshakeHandler.class);
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
HttpSession httpSession = getSession(request);
String user = (String)httpSession.getAttribute("loginName");
if(StringUtils.isEmpty(user)){
log.error("未登陸系統,禁止登陸websocket!");
return null;
}
log.info(" MyDefaultHandshakeHandler login = " + user);
return new MyPrincipal(user);
}
private HttpSession getSession(ServerHttpRequest request) {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
return serverRequest.getServletRequest().getSession(false);
}
return null;
}
}
複製代碼
MyPrincipal 定義本身的Principalweb
public class MyPrincipal implements Principal {
private String loginName;
public MyPrincipal(String loginName){
this.loginName = loginName;
}
@Override
public String getName() {
return loginName;
}
}
複製代碼
配置websocket 在registerStompEndpoints中將咱們MyPrincipalHandshakeHandler 和AuthHandshakeInterceptor 配置到服務中 configureMessageBroker方法配置rabbitmq信息,這裏略spring
@Configuration
// 此註解開使用STOMP協議來傳輸基於消息代理的消息,此時能夠在@Controller類中使用@MessageMapping
@EnableWebSocketMessageBroker
public class WebSocketRabbitMQMessageBrokerConfigurer extends AbstractWebSocketMessageBrokerConfigurer {
@Autowired
private MyPrincipalHandshakeHandler myDefaultHandshakeHandler;
@Autowired
private AuthHandshakeInterceptor sessionAuthHandshakeInterceptor;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws/icc/websocket")
.addInterceptors(sessionAuthHandshakeInterceptor)
.setHandshakeHandler(myDefaultHandshakeHandler)
.withSockJS();
}
….
}
複製代碼
TestMQCtl: 登陸到模擬發送頁面:send.jsp 咱們使用SimpMessagingTemplate 對象的convertAndSendToUser向指定用戶的/topic/demo發送消息瀏覽器
@Autowired
private SimpMessagingTemplate template;
/**
* 發送頁面
*/
@RequestMapping(value = "send")
public String sendMq2UserPage(String msg, String userName){
return "websocket/sendtouser/send";
}
/**
* 向執行用戶發送請求
*/
@RequestMapping(value = "send2user")
@ResponseBody
public int sendMq2User(String msg, String name){
System.out.println("===========" + msg + "=======" + name);
RequestMessage demoMQ = new RequestMessage();
demoMQ.setName(msg);
template.convertAndSendToUser(name, "/topic/demo", JSON.toJSONString(demoMQ));
return 0;
}
複製代碼
send.jsp 模擬發送頁面bash
<form action="login" method="post">
接收者用戶:<input type="text" id="name" name="name" value="<%=session.getAttribute("loginName") %>" />
<p>
消息內容:<input type="text" id="msg" name="msg" />
<p>
<input type="button" id="send" value="發送" />
</form>
<script src="/websocket/jquery.js"></script>
<script type=text/javascript>
$("#send").click(function(){
$.post("send2user",
{
name: $('#name').val(),
msg: $('#msg').val()
},
function(data, status){
alert("Data: " + data + "\nStatus: " + status);
});
});
</script>
複製代碼
登陸 http://127.0.0.1:8080/ws/login,使用xiaoming登陸,並提交
點擊鏈接,若是鏈接變灰色,則登陸websocket成功
登陸模擬發送頁面http://127.0.0.1:8080/ws/send,向xiaoming發送test-msg
此時頁面收到信息:
打開兩個不一樣的瀏覽器,分別使用xiaoming1,xiaoming2登陸系統, 使用模擬界面向xiaoming1發送消息,則只有xiaoming1收到 使用模擬界面向xiaoming2發送消息,則只有xiaoming2收到
###結論: 咱們已經實現向特定的用戶發送消息的功能
全部的詳細代碼見github代碼,請儘可能使用tag v0.23,不要使用master,由於master一直在變,不能保證文章中代碼和github上的代碼一直相同