Spring Security 6中JWT用户部分权限失效问题排查求助
问题原因分析
你的核心问题出在JWT权限解析的字符串处理逻辑上:
当你从claims中获取permissions时,直接调用String.valueOf(claims.get("permissions"))将List类型的权限列表转成了字符串,最终得到的格式是[权限1, 权限2, ..., 权限n](带方括号)。
随后用AuthorityUtils.commaSeparatedStringToAuthorityList()解析时,会把每个分割后的元素当成权限:
- 靠前的权限会被解析成
SFG_UI_SCT_ENTITY_TRANSMIT(正常格式) - 最后一个权限会被解析成
FB_UI_TRUSTED_CERTS_DELETE](多了右括号])
这就是为什么最后一个权限无法通过hasAuthority()校验的原因。
解决方案
修改JwtAuthenticationFilter中解析权限的逻辑,直接将claims中的permissions强转为List
@RequiredArgsConstructor @Slf4j public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtSettings settings; private static final String AUTHENTICATION_HEADER_NAME = "Authorization"; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authHeader = request.getHeader(AUTHENTICATION_HEADER_NAME); String jwt = null; if (authHeader != null && authHeader.startsWith("Bearer ")) { jwt = authHeader.substring("Bearer ".length()); } if (jwt != null) { try { final Claims claims = Jwts.parserBuilder() .setSigningKey(getSignKey()) .build() .parseClaimsJws(jwt) .getBody(); final String username = claims.get("sub", String.class); // 直接强转为List<String>,避免生成带方括号的字符串 final List<String> permissionList = claims.get("permissions", List.class); // 将权限列表转为GrantedAuthority集合 Collection<? extends GrantedAuthority> authorities = permissionList.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); final Authentication auth = new UsernamePasswordAuthenticationToken(username, null, authorities); SecurityContextHolder.getContext().setAuthentication(auth); } catch (Exception e) { throw new BadCredentialsException("Invalid token received"); } } filterChain.doFilter(request, response); } private SecretKey getSignKey() { final byte[] keyBytes = settings.getTokenSigningKey().getBytes(); return Keys.hmacShaKeyFor(keyBytes); } }
额外建议(Spring Security 6官方JWT集成方案)
你当前自定义Filter的方式可行,但Spring Security 6提供了更简洁的官方JWT集成方案,减少自定义代码维护成本:
- 添加
spring-security-oauth2-resource-server依赖 - 配置JwtDecoder Bean:
@Bean public JwtDecoder jwtDecoder() { return NimbusJwtDecoder.withSecretKey(getSignKey()).build(); } private SecretKey getSignKey() { final byte[] keyBytes = settings.getTokenSigningKey().getBytes(); return Keys.hmacShaKeyFor(keyBytes); }
- 修改SecurityConfig,替换自定义Filter为官方JWT认证:
@Bean public SecurityFilterChain apiSecurityfilterChain(HttpSecurity http) throws Exception { return http .csrf(csrf -> csrf.disable()) .cors(corsCustomizer ->corsCustomizer.configurationSource(request -> { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedOrigins(Collections.singletonList("http://localhost:9080")); corsConfiguration.setAllowedMethods(Collections.singletonList("*")); corsConfiguration.setAllowCredentials(true); corsConfiguration.setExposedHeaders(Arrays.asList("Authorization")); corsConfiguration.addAllowedHeader("Content-Disposition"); corsConfiguration.addAllowedHeader("Access-Control-Allow-Headers"); corsConfiguration.addAllowedHeader("Access-Control-Expose-Headers"); corsConfiguration.addExposedHeader("Content-Disposition"); corsConfiguration.setMaxAge(3600L); return corsConfiguration; })) .authorizeHttpRequests(auth -> { auth.requestMatchers("/api/auth/signin", "/api/auth/sso", "/api/version/*") .permitAll() .anyRequest() .authenticated(); }) // 替换自定义Filter为官方JWT资源服务器配置 .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .build(); }
- 实现JwtAuthenticationConverter,映射JWT中的权限:
@Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); grantedAuthoritiesConverter.setAuthoritiesClaimName("permissions"); grantedAuthoritiesConverter.setAuthorityPrefix(""); // 不需要权限前缀可设为空 JwtAuthenticationConverter converter = new JwtAuthenticationConverter(); converter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); return converter; }
内容的提问来源于stack exchange,提问作者peter greaves
相关产品推荐
相关产品推荐

