springboot websocket 实现消息推送
什么是WebSocket
WebSocket为浏览器和服务器之间提供了双工异步通信功能,也就是说我们可以利用浏览器给服务器发送消息,服务器也可以给浏览器发送消息,目前主流浏览器的主流版本对WebSocket的支持都算是比较好的,但是在实际开发中使用WebSocket工作量会略大,而且增加了浏览器的兼容问题,这种时候我们更多的是使用WebSocket的一个子协议stomp,利用它来快速实现我们的功能。
1.第一步
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
2.创建websocketConfig
@Configuration
@EnableWebSocketMessageBroker
@ComponentScan("com.sensetime.fis.senseguard")
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Autowired
private PrincipalHandshakeHandler myDefaultHandshakeHandler;
@Autowired
private WebSocketInterceptor sessionAuthHandshakeInterceptor;
/**
* 配置消息代理类
*
* @param registry
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 广播式应配置一个/topic消息代理
// 推送消息前缀
registry.enableSimpleBroker("/topic");
// 应用请求前缀
registry.setApplicationDestinationPrefixes("/app");
// 推送用户前缀
registry.setUserDestinationPrefix("/user");
}@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
// 定义ws连接入口
stompEndpointRegistry.addEndpoint("/websocket").setAllowedOrigins("*").addInterceptors(sessionAuthHandshakeInterceptor)
.setHandshakeHandler(myDefaultHandshakeHandler).withSockJS()
.setInterceptors(new WebSocketInterceptor());
}@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
3.握手前配置 PrincipalHandshakeHandler
@Component
public class PrincipalHandshakeHandler extends DefaultHandshakeHandler {
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler,
Map<String, Object> attributes) {
HttpSession httpSession = getSession(request);
String token = (String) httpSession.getAttribute("token");if (!StringUtils.isEmpty(token)) {
return new ExportPrincipal(token);
}
return null;
}private HttpSession getSession(ServerHttpRequest request) {
ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
return serverRequest.getServletRequest().getSession(true);
}
4.拦截器配置 WebSocketInterceptor
/**
*
* @ClassName: MyWebSocketInterceptor
* @Description: 创建握手 此类用来获取登录用户信息并交由websocket管理
* @author libo
* @date 2017年9月26日 上午10:31:30
* HandshakeInterceptor WebSocket握手请求的拦截器. 检查握手请求和响应, 对WebSocketHandler传递属性
*/
@Component
public class WebSocketInterceptor extends TextWebSocketHandler implements HandshakeInterceptor {private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 在握手之前执行该方法, 继续握手返回true, 中断握手返回false. 通过attributes参数设置WebSocketSession的属性
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
HttpSession httpSession = getSession(request);
String token = getToken(request.getURI().toString());
httpSession.setAttribute("token", token);
return true;
}/**
* 在握手之后执行该方法. 无论是否握手成功都指明了响应状态码和相应头.
*/
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
String token = getToken(request.getURI().toString());
logger.info("TOKEN为【{}】的用户建立消息连接已完成", token);
}private String getToken(String uri) {
int index = uri.indexOf("accessToken=");
return uri.substring(index + 12, uri.length());
}/**
* 用户进入系统监听
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
logger.info("xxx用户进入系统。。。");
logger.info("用户信息:" + session.getAttributes());
System.out.println(session.getAttributes());
System.out.println("xxx用户进入系统。。。");Map<String, Object> map = session.getAttributes();
for (String key : map.keySet()) {
System.out.println("key:" + key + " and value:" + map.get(key));
}
}@SuppressWarnings("unused")
private HttpSession getSession(ServerHttpRequest request) {
ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
return serverRequest.getServletRequest().getSession(true);
}
5.推送
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;simpMessagingTemplate.convertAndSendToUser(task.getWebsocketId(), "/topic/records/subscribe", result);
后端整个流程就是这样了,前端简历连接订阅完成后,就可以接受后端给的推送消息。