mirror of
https://github.com/youlaitech/youlai-boot.git
synced 2026-09-02 22:44:53 +08:00
Merge pull request #17 from yofchio/fix/jwt-security-vulnerabilities
fix: 修复 JWT 算法降级(alg:none)绕过鉴权及多处 JWT 安全漏洞
This commit is contained in:
@@ -187,7 +187,13 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
throw new BadCredentialsException("Token 为空");
|
||||
}
|
||||
|
||||
// 解析并验证 Token
|
||||
// 校验 Token 有效性(算法+签名+过期时间+黑名单)
|
||||
if (!tokenManager.validateToken(token)) {
|
||||
log.warn("⚠ 非法连接请求:Token 无效或已过期");
|
||||
throw new BadCredentialsException("Token 无效或已过期");
|
||||
}
|
||||
|
||||
// 解析 Token
|
||||
Authentication authentication;
|
||||
try {
|
||||
authentication = tokenManager.parseToken(token);
|
||||
|
||||
@@ -49,6 +49,8 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class JwtTokenManager implements TokenManager {
|
||||
|
||||
private static final String ALLOWED_ALGORITHM = "HS256";
|
||||
|
||||
private final SecurityProperties securityProperties;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private final byte[] secretKey;
|
||||
@@ -91,6 +93,14 @@ public class JwtTokenManager implements TokenManager {
|
||||
public Authentication parseToken(String token) {
|
||||
|
||||
JWT jwt = JWTUtil.parseToken(token);
|
||||
|
||||
if (!isAlgorithmAllowed(jwt)) {
|
||||
throw new BusinessException(ResultCode.ACCESS_TOKEN_INVALID);
|
||||
}
|
||||
if (!jwt.setKey(secretKey).verify()) {
|
||||
throw new BusinessException(ResultCode.ACCESS_TOKEN_INVALID);
|
||||
}
|
||||
|
||||
JSONObject payloads = jwt.getPayloads();
|
||||
SysUserDetails userDetails = new SysUserDetails();
|
||||
userDetails.setUserId(payloads.getLong(JwtClaimConstants.USER_ID)); // 用户ID
|
||||
@@ -164,6 +174,11 @@ public class JwtTokenManager implements TokenManager {
|
||||
*/
|
||||
private boolean validateToken(String token, boolean validateRefreshToken) {
|
||||
JWT jwt = JWTUtil.parseToken(token);
|
||||
|
||||
if (!isAlgorithmAllowed(jwt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查 Token 是否有效(验签 + 是否过期)
|
||||
boolean isValid = jwt.setKey(secretKey).validate(0);
|
||||
|
||||
@@ -219,6 +234,11 @@ public class JwtTokenManager implements TokenManager {
|
||||
token = token.substring(SecurityConstants.BEARER_TOKEN_PREFIX.length());
|
||||
}
|
||||
JWT jwt = JWTUtil.parseToken(token);
|
||||
|
||||
if (!isAlgorithmAllowed(jwt) || !jwt.setKey(secretKey).verify()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject payloads = jwt.getPayloads();
|
||||
String jti = payloads.getStr(JWTPayload.JWT_ID);
|
||||
Integer expirationAt = payloads.getInt(JWTPayload.EXPIRES_AT);
|
||||
@@ -400,4 +420,12 @@ public class JwtTokenManager implements TokenManager {
|
||||
return JWTUtil.createToken(payload, secretKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验JWT算法类型是否在允许的白名单中,防止 alg:none 等算法降级攻击
|
||||
*/
|
||||
private boolean isAlgorithmAllowed(JWT jwt) {
|
||||
Object algorithm = jwt.getHeader("alg");
|
||||
return algorithm != null && ALLOWED_ALGORITHM.equals(algorithm.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ security:
|
||||
access-token-time-to-live: 7200 # 访问令牌 有效期(单位:秒),默认 2 小时,-1 表示永不过期
|
||||
refresh-token-time-to-live: 604800 # 刷新令牌有效期(单位:秒),默认 7 天,-1 表示永不过期
|
||||
jwt:
|
||||
secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789 # JWT密钥(HS256算法至少32字符)
|
||||
secret-key: SecretKey012345678901234567890123456789012345678901234567890123456789 # JWT密钥(HS256算法至少32字符) 【安全警告】生产环境必须更换此默认密钥,否则攻击者可直接伪造合法Token
|
||||
redis-token:
|
||||
allow-multi-login: true # 是否允许多设备登录
|
||||
# 安全白名单路径,仅跳过 AuthorizationFilter 过滤器,还是会走 Spring Security 的其他过滤器(CSRF、CORS等)
|
||||
|
||||
Reference in New Issue
Block a user