在创建Vue+Springboot前后端分离项目时,需要使用Websocket进行通讯,但是后端报了如下错误,不是每次但是经常
java.lang.IllegalStateException: WebSocketSession not yet initialized
at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-5.3.19.jar:5.3.19]
at org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession.getPrincipal(WebSocketServerSockJsSession.java:87) ~[spring-websocket-5.3.19.jar:5.3.19]
前端Websocket设置
vue.config.js
//配置websocket代理
proxyObj["/elitehrserver/ws"]={
/*
目前比较来看,无论是http还是ws都会后端都会偶尔出现WebSocketSession not yet initialized错误
*/
target: "http://localhost:8081",
changeOrigin: true
};
websocket的相关使用
//websocket连接到后端服务器,并订阅消息
connect(context){
//创建连接后台服务器并返回实例
context.state.stomp=Stomp.over(new SockJS(baseUrl+"/ws/ep"));
let token=window.sessionStorage.getItem("token");
//连接
context.state.stomp.connect({"Auth-Token":token},success=>{
//订阅消息(接口)并打印消息 /queue/chat 前面必须加 /user 这是规定
context.state.stomp.subscribe("/user/queue/chat",msg=>{
let recieveMsg=JSON.parse(msg.body);
//如果不是当前对话用户或当前用户没和谁对话则展示消息通知
if(!context.state.currentSession||recieveMsg.from!==context.state.currentSession.username){
Notification({
title: "【"+recieveMsg.fromNickName+"】",
message: recieveMsg.content.length>10?recieveMsg.content.substr(0,10):recieveMsg.content,
position: 'bottom-right',
iconClass:"fa fa-comments"
});
//添加消息红点
Vue.set(context.state.idDot,context.state.currentAdmin.username+"#"+recieveMsg.from,true);
}
recieveMsg.notSelf=true;
recieveMsg.to=recieveMsg.from;
//添加同步消息
context.commit("addMessage",recieveMsg);
})
},error=>{
})
}
后端配置
/**
* websocket配置类
* @author 刘昌兴
*
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Value("${jwt.tokenHead}")
private String tokenHead;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
/**
* 添加endpoint,这样网页可以通过websocket连接上服务器
* 也就是我们配置websocket的服务地址,并可以指定是否可以使用socketJS
* @author 刘昌兴
*
* @param registry
* @return
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
/*
* 1.将/ws/ep作为stomp的端点,用户连接了这个端点就可以进行websocket通讯,支持socketJS
* 2.配置允许跨域
* 3.支持socketJS访问
*/
registry.addEndpoint("/ws/ep").setAllowedOriginPatterns("*").withSockJS();
}
/**
* 输入通道参数配置
* @author 刘昌兴
*
* @param registration
* @return
*/
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor= MessageHeaderAccessor.getAccessor(message,StompHeaderAccessor.class);
//判断是否为连接,如果是则需要获取token,并设置用户对象,用于后续发送消息是获得发送来源等,而不是进行token认证,因为已经放行了/ws/**
if(StompCommand.CONNECT.equals(accessor.getCommand())){
String token=accessor.getFirstNativeHeader("Auth-Token");
if(StringUtils.hasLength(token)){
String authToken=token.substring(tokenHead.length());
String username=jwtTokenUtil.getUserNameFromToken(authToken);
//如果token中存在用户名
if(StringUtils.hasLength(username)){
UserDetails userDetails=userDetailsService.loadUserByUsername(username);
//验证token是否有效,重新设置用户对象
if(jwtTokenUtil.validateToken(authToken,userDetails)){
UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
accessor.setUser(authenticationToken);
}
}
}
}
return message;
}
});
}
/**
* 配置消息代理
* @author 刘昌兴
* @param registry
* @return
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//配置代理域,可以配置多个,配置代理目的地前缀为/queue,可以在配置域上向客户端推送消息
registry.enableSimpleBroker("/queue");
}
}