面试题答案
一键面试跨域访问实现
- 实现原理:通过在WebSocket服务器端设置响应头,允许特定来源的请求访问。
- 技术框架:Java WebSocket开发框架如Tyrus或Spring WebSocket都可用于设置跨域。
- 关键代码示例(以Spring 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.setApplicationDestinationPrefixes("/app");
config.enableSimpleBroker("/topic");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket-endpoint")
.setAllowedOrigins("*") // 设置允许的跨域来源,这里用 * 表示所有来源,实际应用需替换为具体来源
.withSockJS();
}
}
负载均衡实现
- 实现原理:使用反向代理服务器(如Nginx)来分发WebSocket连接请求到多个后端服务器实例。Nginx可以基于多种算法(如轮询、IP哈希等)进行负载均衡。
- 技术框架:Nginx作为反向代理服务器实现负载均衡。
- 关键配置示例(Nginx配置文件):
http {
upstream websocket_backends {
server backend1.example.com:8080;
server backend2.example.com:8080;
# 可以继续添加更多后端服务器
}
server {
listen 80;
server_name your_domain.com;
location /websocket-endpoint {
proxy_pass http://websocket_backends;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
}
安全认证实现
- 实现原理:在建立WebSocket连接前,进行身份验证。可以使用JWT(JSON Web Token)等技术,前端在连接请求时携带JWT,后端验证JWT的有效性。
- 技术框架:使用JJWT库来处理JWT相关操作。
- 关键代码示例(以Spring WebSocket结合JWT为例):
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
@Component
public class WebSocketChannelInterceptor implements ChannelInterceptor {
private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String jwt = accessor.getFirstNativeHeader("Authorization");
if (jwt != null && jwt.startsWith("Bearer ")) {
jwt = jwt.substring(7);
try {
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(jwt)
.getBody();
// 验证成功,可以进行后续操作,如设置用户信息到会话等
accessor.setUser(new org.springframework.security.core.userdetails.User(claims.getSubject(), "", null));
} catch (Exception e) {
// 验证失败,拒绝连接
throw new RuntimeException("Invalid JWT");
}
} else {
throw new RuntimeException("Missing JWT");
}
}
return message;
}
}
并在配置类中注册该拦截器:
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;
import org.springframework.messaging.simp.config.ChannelRegistration;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app");
config.enableSimpleBroker("/topic");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket-endpoint")
.setAllowedOrigins("*")
.withSockJS();
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new WebSocketChannelInterceptor());
}
}