我正在尝试使用Spring Boot (1.5.13)实现一个websocket。
消息传递工作正常,但大约30分钟后,服务器终止连接(原因1008 -“此连接是在已结束的已验证的HTTP会话下建立的”)。我尝试过设置不同的超时,但似乎没有任何效果。
@Service
@RequiredArgsConstructor
@Slf4j
public class OCPPSocketHandler extends TextWebSocketHandler {
@Override
public void handleTextMessage(WebSocketSession webSocketSession, TextMessage textMessage)
throws IOException {
...
}
}
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
public static final String ENDPOINT = "/pp/v2.0";
@Autowired
private CustomSocketHandler socketHandler;
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(
new CustomExceptionWebSocketHandlerDecorator(socketHandler), ENDPOINT
)
.setAllowedOrigins("*");
}
}application.properties:
#6h as milliseconds
server.connection-timeout=3600000
server.servlet.session.timeout=6h每30分钟发送一次TextMessage (WebSocket)以保持连接处于活动状态。
我见过this question about session timeouts,但我看不出有什么解决方案
发布于 2019-07-29 22:37:07
我发现我的WebSocket在30分钟后也关闭了。
根本原因是在SpringBoot中,默认情况下http会话将在30分钟后关闭。
在SpringBoot配置属性中,server.servlet.session.timeout将更改默认行为,但可能有some limit。
此外,WebSocket连接有保持活动状态的乒乓消息,因此在乒乓停止之前,永远不要关闭连接。
经过一些跟踪,我找到了一个解决这个问题的方法:
io.undertow.server.session.InMemorySessionManager.SessionImpl在我的例子中。我们可以看到io.undertow.server.session.InMemorySessionManager.SessionImpl#setMaxInactiveInterval将重置计时器。setMaxInactiveInterval,连接就永远不会关闭。下面是我的实现:
将HandshakeRequest存储到配置器中的javax.websocket.EndpointConfig#getUserProperties中javax.websocket.server.ServerEndpointConfig.Configurator#modifyHandshake
javax.websocket.Session#getUserProperties获取HandshakeRequest,然后HttpSession httpSession = (HttpSession) handshakeRequest.getHttpSession();
httpSession.setMaxInactiveInterval((int) (session.getMaxIdleTimeout() / 1000));这就是全部,希望它能有所帮助。
发布于 2018-05-29 23:11:22
如果服务器或客户端之间的任何连接关闭了该连接,则可以关闭该连接,如果防火墙注意到所建立的连接上没有任何活动,也可以关闭该连接。
因此,您还需要在客户端检查超时。30分钟是这种连接的默认超时,这是合理的,因此它看起来在客户端使用默认值。
此外,定期检查连接的状态是一个很好的设计,例如发送一种ping (来自客户端的消息) /pong (来自服务器的响应)消息。例如,如果你每分钟做一次,你就对连接状态有了一个概念,连接永远不会因为不活动而关闭。
发布于 2021-07-24 21:23:15
在我的例子中,timeout of HttpSession in SpringBoot设置为server.servlet.session.timeout: 1 (1 HttpSession)
@Override protected void configure(HttpSecurity http)抛出异常{ http.csrf().disable();http.antMatcher("/ocpp/**") .authorizeRequests() .anyRequest().permitAll()//.hasAnyRole("CS","SYSTEM_ADMIN") .and().httpBasic();}
String id = "testuser";String pw = "1234";String idpw =新建授权;Map httpHeaders =新授权,String>();httpHeaders.put(“String(Base64.getEncoder().encode((id+":"+pw).getBytes()));”,"Basic "+idpw);WebSocketClient webSocketClient =新WebSocketClient(新URI("ws://localhost:9112/ocpp/testuser"),新Draft_6455(Collections.emptyList(),Collections.singletonList(新协议(“ocpp2.0.1”),httpHeaders ){ ...
在这种情况下,如果端点是受保护资源,则在终止HttpSession时发生websocket关闭1008。
Reference:阅读第7.2段
**解决方案是在websocket addInterceptor中直接实现http基本处理。(Spring安全httpBasic未使用) **
在WebSocketConfig.java中添加addInterceptor
.addInterceptors(new HandshakeInterceptor() {
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
log.debug("===============================================================");
log.debug("beforeHandshake[]"+attributes);
ServletServerHttpRequest ssreq = (ServletServerHttpRequest)serverHttpRequest;
ServletServerHttpResponse ssres = (ServletServerHttpResponse)serverHttpResponse;
HttpServletRequest req = ssreq.getServletRequest();
HttpServletResponse res = ssres.getServletResponse();
HttpSession session = req.getSession();
log.debug("session["+session.getId());
log.debug("session["+session.getMaxInactiveInterval());
//authentication
try {
String header = req.getHeader("Authorization");
log.debug("header[" + header + "]");
if (header == null) {
log.debug("The Authorization header is empty");
// throw new BadCredentialsException("The Authorization header is empty");
} else {
header = header.trim();
if (!StringUtils.startsWithIgnoreCase(header, "Basic")) {
log.debug("The Authorization header does not start with Basic.");
} else if (header.equalsIgnoreCase("Basic")) {
throw new BadCredentialsException("Empty basic authentication token");
} else {
byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
byte[] decoded;
try {
decoded = Base64.getDecoder().decode(base64Token);
} catch (IllegalArgumentException var8) {
throw new BadCredentialsException("Failed to decode basic authentication token");
}
String token = new String(decoded, "UTF-8");
int delim = token.indexOf(":");
if (delim == -1) {
throw new BadCredentialsException("Invalid basic authentication token");
} else {
log.info("TOKEN [" +token+"]");
String principal = token.substring(0, delim);
String credencial = token.substring(delim + 1);
//your
if(principal.equals("testuser") && credencial.equals("1234")){
log.debug("login OK");
}else{
throw new BadCredentialsException("Invalid basic authentication token");
}
}
}
}
}catch(Exception e){
log.error("Basic Authentication error", e);
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
res.addHeader("WWW-Authenticate", "Basic realm=" + "Realm" + "");
PrintWriter pw = res.getWriter();
pw.println("Invalid status code received: 401 Status line: HTTP/1.1 401");
return false;
}
return true;
}https://stackoverflow.com/questions/50587573
复制相似问题