Обзор
отличается от вышеперечисленногоСемнадцатая серия Spring Boot Spring Boot интегрирует веб-сокет и использует RabbitMQ в качестве брокера сообщений.В этой статье мы представляем реализацию отправки сообщений конкретным пользователям через веб-сокет Spring. Содержание этой статьи следующее: 1. Сначала реализуем простую функцию авторизации, вот необходимое условие для отправки сообщения конкретному пользователю 2. После того, как пользователь войдет в систему, он может войти в веб-сокет и переписать MyPrincipal. 3. Реализовать функцию отправки сообщений конкретным пользователям 4. Тест
Сначала реализуйте простую функцию входа в систему, которая необходима для отправки сообщения конкретному пользователю.
TestMQCtl: класс управленияОбеспечьте имитацию входа в систему, перейдите на страницу веб-сокета после успешного входа в систему
/**
* 模拟登录 */
@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Простая форма, укажите запрос на вход в систему и перейдите на страницу ws-sendtouser-rabbitmq.jsp.
<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Подключитесь к вебсокету и подпишитесь на сообщение.Предыдущая статья этого jsp была введена и подробно здесь описываться не будет. Страница запускает веб-сокет в /ws/icc/websocket, а затем подписывается на сообщения /user/topic/demo.
<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>
После входа пользователя в систему он может войти в вебсокет и переписать MyPrincipal
AuthHandshakeInterceptorAuthHandshakeInterceptor является подклассом HandshakeInterceptor. Перед рукопожатием веб-сокета определите, вошел ли текущий пользователь в систему. Если вы не вошли в систему, вход через веб-сокет не разрешен
@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;
}
}
MyPrincipalHandshakeHandlerMyPrincipalHandshakeHandler является подклассом DefaultHandshakeHandler, который обрабатывает запросы веб-сокетов.Здесь мы только переписываем метод defineUser для создания собственного принципала.Здесь мы используем тег loginName для входа пользователя вместо значения по умолчанию.
@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Определите своего собственного принципала
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, немного здесь
@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 Мы используем convertAndSendToUser объекта SimpMessagingTemplate для отправки сообщения указанному пользователю /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Имитация страницы отправки
<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 и отправьте
Нажмите на ссылку, если соединение серое, веб-сокет успешного входа
Войдите на смоделированную страницу отправки http://127.0.0.1:8080/ws/send и отправьте тестовое сообщение сяомину.
В этот момент страница получает сообщение:
В фиктивном интерфейсе, если мы отправим информацию другим пользователям, этот интерфейс не получит информацию.Второй тест:
Откройте два разных браузера и используйте xiaoming1 и xiaoming2 для входа в систему соответственно. Используйте интерфейс моделирования, чтобы отправить сообщение xiaoming1, только xiaoming1 получит его Отправьте сообщение на сяомин2 с помощью аналогового интерфейса, его получит только сяомин2
###в заключении: Мы реализовали возможность отправить сообщение конкретному пользователю
код
Смотрите код github для всех подробных кодов, пожалуйста, используйте как можно большетег v0.23, не используйте мастер, потому что мастер всегда меняется, нет гарантии, что код в статье и код на гитхабе всегда будут одинаковыми