WebSocket鉴权 前后端配置
1、 前端修改:连接时携带Token
在前端建立WebSocket连接时,将Token作为查询参数附加到URL中。
const token = localStorage.getItem('auth_token');
//示例地址
const ws_url = `ws://127.18.177.208:8080/websocket/${getRandomString(8)}?token=${encodeURIComponent(token)}`;
2、 创建鉴权配置类(处理Spring依赖)
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
3、创建自定义鉴权配置器
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import java.util.List;
import java.util.Map;
public class WebSocketAuthConfigurator extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
// 从请求参数获取Token
Map<String, List<String>> params = request.getParameterMap();
List<String> tokens = params.get("token");
String token = (tokens != null && !tokens.isEmpty()) ? tokens.get(0) : null;
// 从Spring容器获取JWT工具类
JwtUtil jwtUtil = SpringContext.getBean(JwtUtil.class);
if (token == null || !jwtUtil.validateToken(token)) {
throw new RuntimeException("连接未授权,Token验证失败");
}
}
}
4、 修改WebSocketServer类配置
@ServerEndpoint(value = "/websocket/{sid}", configurator = WebSocketAuthConfigurator.class)
public class WebSocketServer {
// 原有代码保持不变...
}
5、网关配置调整(移除白名单)
6、JWT 工具类
如果 auth 模块含有 token 权限验证方法可直接替换
以下为示例代码
1、引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
2、JWT 示例代码
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtUtil {
// 从配置文件中读取密钥和有效期
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
// 生成安全密钥
private SecretKey getSecretKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
/**
* 生成JWT Token
* @param claims 自定义声明(建议至少包含用户标识)
* @return Token字符串
*/
public String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims) // 自定义声明
.setIssuedAt(new Date()) // 签发时间
.setExpiration(new Date(System.currentTimeMillis() + expiration)) // 过期时间
.signWith(getSecretKey(), SignatureAlgorithm.HS256) // 签名算法
.compact();
}
/**
* 验证Token有效性
* @param token 需要验证的Token
* @return 是否有效
*/
public boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(getSecretKey())
.build()
.parseClaimsJws(token);
return true;
} catch (SecurityException e) {
log.error("Invalid JWT signature: {}", e.getMessage());
} catch (MalformedJwtException e) {
log.error("Invalid JWT token: {}", e.getMessage());
} catch (ExpiredJwtException e) {
log.error("JWT token is expired: {}", e.getMessage());
} catch (UnsupportedJwtException e) {
log.error("JWT token is unsupported: {}", e.getMessage());
} catch (IllegalArgumentException e) {
log.error("JWT claims string is empty: {}", e.getMessage());
}
return false;
}
/**
* 解析Token获取声明信息
* @param token Token字符串
* @return 声明集合
*/
public Claims parseToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSecretKey())
.build()
.parseClaimsJws(token)
.getBody();
}
/**
* 获取指定声明
* @param token Token字符串
* @param claimKey 声明键名
* @return 声明值
*/
public <T> T getClaim(String token, String claimKey, Class<T> requiredType) {
final Claims claims = parseToken(token);
return claims.get(claimKey, requiredType);
}
/**
* 生成包含用户ID的Token(示例方法)
* @param userId 用户ID
* @return Token字符串
*/
public String generateTokenWithUserId(String userId) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
return generateToken(claims);
}
/**
* 从Token中获取用户ID(示例方法)
* @param token Token字符串
* @return 用户ID
*/
public String getUserIdFromToken(String token) {
return getClaim(token, "userId", String.class);
}
}
3、在 application.properties
或 application.yml
中添加配置
# JWT配置
jwt.secret=your-256-bit-secret-key-base64-encoded-here-1234567890
jwt.expiration=86400000 # 24小时(单位:毫秒)