引自免费微信小程序:皆我百晓生
在Spring Boot中,如果你想要在WebSocket会话中实现HTTP Session认证和拦截,你需要处理WebSocket特定的生命周期方法,并且不是通过WebMvcConfigurer
和拦截器(HandlerInterceptor
)来完成的。因为WebSocket连接并不依赖于HTTP请求/响应模型,所以传统的MVC拦截器不适用于WebSocket。
为了解决这个问题,你可以创建一个自定义的TextWebSocketHandler
子类并实现认证逻辑。这里是一个详细的步骤和示例代码:
- 首先,创建一个自定义的WebSocket处理器:
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
@Component
public class SecureWebSocketHandler extends TextWebSocketHandler {
private static final Logger logger = LoggerFactory.getLogger(SecureWebSocketHandler.class);
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String sessionId = (String) session.getHandshakeAttributes().get(HttpHeaders.SESSION_ID);
HttpSession httpSession = getSessionFromHttpRequest(sessionId);
if (httpSession == null || !isUserAuthenticated(httpSession)) {
logger.warn("Closing unauthenticated WebSocket connection: {}", session.getId());
session.close();
return;
}
super.afterConnectionEstablished(session);
}
// Implement your logic to get the HTTP session from the given session ID
private HttpSession getSessionFromHttpRequest(String sessionId) {
// Here you can use a custom method or library like Spring Security's
// HttpSessionStrategy to retrieve the HTTP session based on the session ID.
// For simplicity, let's assume we have a direct access to the sessions map:
Map<String, HttpSession> sessionsMap = ...; // Replace with your implementation
return sessionsMap.get(sessionId);
}
// Implement your logic to check if the user is authenticated in the HTTP session
private boolean isUserAuthenticated(HttpSession httpSession) {
// For example, if using Spring Security, you could do:
// Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// return authentication != null && !authentication.getName().equals("anonymousUser");
// For simplicity, let's assume that any non-null session means the user is authenticated
return httpSession != null;
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// Your business logic here
logger.info("Received message: {}", message.getPayload());
session.sendMessage(new TextMessage("Echo: " + message.getPayload()));
}
}
- 在配置文件中启用WebSocket支持:
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
}
- 为了处理客户端的连接请求,你可能还需要一个简单的控制器:
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.web.bind.annotation.Controller;
@Controller
public class WebSocketController {
@MessageMapping("/connect")
@SendTo("/topic/messages")
public String connect(String message) {
return "Connected: " + message;
}
}
这样,每次新的WebSocket连接建立时,都会调用SecureWebSocketHandler
中的afterConnectionEstablished()
方法进行认证。如果用户未通过认证,将关闭WebSocket连接。
注意:这个例子没有包含具体的HTTP Session获取逻辑,你需要根据你的应用环境(例如是否使用Spring Security等)来实现这部分功能。