
本文介绍在缺乏 JWT 签名密钥时,通过手动 Base64 解码 JWT 载荷(payload)提取 roles 声明,并结合 Spring Security 实现轻量级角色授权的方法,强调其适用场景与安全风险。
本文介绍在缺乏 jwt 签名密钥时,通过手动 base64 解码 jwt 载荷(payload)提取 `roles` 声明,并结合 spring security 实现轻量级角色授权的方法,强调其适用场景与安全风险。
JWT(JSON Web Token)由三部分组成:Header、Payload 和 Signature,以 . 分隔。标准库(如 JJWT)在调用 .parseClaimsJws(token) 时强制校验签名,若未提供合法签名密钥(通常是公钥或对称密钥),解析将失败并抛出异常——这正是你遇到 claims == null 的根本原因。但若业务场景明确接受“信任前端传入的 token 内容”(例如内部系统、可信网关前置鉴权、开发联调阶段),可绕过签名验证,直接解码头部与载荷。
✅ 正确解码 JWT Payload(无签名验证)
JWT 的第二段(Payload)是 Base64Url 编码的 JSON 字符串。注意:必须使用 Base64Url 解码(非标准 Base64),以正确处理 +、/ 和填充 = 的差异。Java 8+ 推荐使用 Base64.getUrlDecoder():
public static Claims parseUnverifiedJwtPayload(String jwtToken) {
String[] parts = jwtToken.split("\.");
if (parts.length != 3) {
throw new IllegalArgumentException("Invalid JWT token format");
}
String payloadB64 = parts[1];
// Base64Url decode → UTF-8 string → JSON parse
String payloadJson = new String(Base64.getUrlDecoder().decode(payloadB64), StandardCharsets.UTF_8);
return Jwts.claims().parse(payloadJson).get();
}
⚠️ 注意事项:
- Base64.getUrlDecoder() 是关键,Base64.getDecoder() 可能因末尾 = 缺失或字符替换失败;
- 此方法不校验签名、不检查过期时间(exp)、不验证 issuer(iss)等任何安全声明,仅作结构化数据提取;
- 若 payload 中 roles 是字符串数组(推荐),解析后可通过 claims.get("roles", List.class) 获取;若是逗号分隔字符串,需额外 split(",") 处理。
? 集成 Spring Security 实现 Role-Based Authorization
Spring Security 不依赖 JWT 库做权限判定,而是基于 Authentication 对象中的 GrantedAuthority。我们可构建一个自定义 JwtAuthenticationFilter,在 doFilterInternal 中:
- 提取请求头中的 Bearer Token;
- 调用上述 parseUnverifiedJwtPayload() 获取 claims;
- 从 claims 提取 roles 并转换为 SimpleGrantedAuthority 列表;
- 构建 UsernamePasswordAuthenticationToken 并设入 SecurityContextHolder。
示例核心逻辑:
List<grantedauthority> authorities = claims.get("roles", List.class).stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.toString().toUpperCase()))
.collect(Collectors.toList());
Authentication auth = new UsernamePasswordAuthenticationToken(
"anonymous", null, authorities
);
SecurityContextHolder.getContext().setAuthentication(auth);</grantedauthority>
随后,在 @Configuration 类中启用方法级权限(如 @PreAuthorize("hasRole('ADMIN')"))或配置 URL 权限:
http.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
);
⚠️ 重要安全警示(必读)
此方案仅适用于严格受控环境,例如:
- 前端与后端同属一个可信域(如内网管理后台);
- JWT 由前端本地生成(如模拟登录),无外部攻击面;
- 网关层已统一完成强身份认证与签名验证,后端仅消费角色信息。
❌ 绝对禁止在生产面向公网的 API 中使用!跳过签名验证等于放弃 JWT 的核心安全保证——完整性与来源可信性。攻击者可任意篡改 roles 字段(如 "roles":["ADMIN"])并重放请求,导致越权访问。
✅ 正确做法应是:向签发方(如 Auth0、Keycloak 或自建 OAuth2 授权服务器)获取其 JWT 公钥(JWKS 端点),并在 Spring Security 中配置 NimbusJwtDecoder.withPublicKey(...) 进行标准验证。
总之,手动解码是调试与过渡手段,而非安全方案。请始终优先推动完整 JWT 验证流程落地。










