无JWT签名密钥时,如何基于JWT角色实现Spring Security授权?
解决方案:无签名密钥下解码JWT并实现Spring Security权限控制
一、问题根源
你当前使用的parseClaimsJws()方法会强制验证JWT签名,没有密钥时会抛出异常(被你捕获后未处理),导致claims始终为空。要跳过签名验证,需使用JJWT专门的无校验解析方式。
二、正确的JJWT解码代码(不验证签名)
适配JJWT 0.11.x及以上版本
该版本提供了官方的无签名验证模式:
String token = "ey....."; Claims claims; try { claims = Jwts.parser() .unsecured() // 启用无签名校验模式,允许解析跳过签名验证的JWT .parseSignedClaims(token) .getPayload(); } catch (Exception e) { e.printStackTrace(); // 不要吞异常,方便排查问题 }
适配JJWT 0.9.x及更低版本
旧版本需手动设置空密钥并处理签名异常:
String token = "ey....."; Claims claims; try { claims = Jwts.parser() .setSigningKey(null) // 设置空密钥 .parseClaimsJws(token) .getBody(); } catch (SignatureException e) { // 捕获签名验证失败异常,改用无签名解析方式 claims = Jwts.parser() .parseClaimsJwt(token) .getBody(); } catch (Exception e) { e.printStackTrace(); }
三、Spring Security集成步骤
1. 自定义JWT认证过滤器
在请求链路中提取JWT、解码并注入权限信息:
public class JwtAuthorizationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = extractTokenFromRequest(request); if (token != null) { Claims claims = decodeJwt(token); if (claims != null) { String username = claims.getSubject(); List<String> roles = claims.get("roles", List.class); // 转换为Spring Security识别的权限格式 Collection<? extends GrantedAuthority> authorities = roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( username, null, authorities); SecurityContextHolder.getContext().setAuthentication(authToken); } } filterChain.doFilter(request, response); } // 从请求头提取JWT令牌 private String extractTokenFromRequest(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (bearerToken != null && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } // 调用前面的JWT解码逻辑 private Claims decodeJwt(String token) { try { return Jwts.parser() .unsecured() .parseSignedClaims(token) .getPayload(); } catch (Exception e) { e.printStackTrace(); return null; } } }
2. 配置Spring Security规则
注册过滤器并定义API权限:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) // 关闭CSRF(适配REST API场景) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/admin/**").hasRole("ADMIN") // 管理员专属接口 .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN") // 用户/管理员可访问 .anyRequest().authenticated() // 其他接口需认证 ) .addFilterBefore(new JwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } }
四、关键注意事项
- 安全风险:跳过签名验证意味着任何伪造的JWT都能通过授权,仅适合内部测试或完全信任的封闭环境,生产环境必须验证签名。
- 版本依赖:确保项目引入正确的JJWT版本,0.11.x版本的Maven依赖如下:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-core</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>
内容的提问来源于stack exchange,提问作者noob555
相关产品推荐
相关产品推荐

