Spring Security验证AWS Cognito JWT角色失败,返回403错误
问题:Spring Security验证AWS Cognito JWT角色时返回403
我正尝试在Spring Security配置中验证来自AWS Cognito用户池的JWT令牌是否包含正确角色,但始终收到403响应。
我的安全配置代码如下:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http .addFilterBefore(customRoleFilter(), UsernamePasswordAuthenticationFilter.class) //TODO:: does not work! .authorizeHttpRequests(auth -> { auth.requestMatchers("/api/v1/products").permitAll(); auth.requestMatchers("/api/v1/products/**").hasRole("USER"); auth.anyRequest().authenticated(); }) .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.decoder(new JWTDecoder()))) //TODO:: also tried to map roles but doesn't work either. .sessionManagement(session -> session.sessionCreationPolicy(STATELESS)) .build(); } @Bean public CustomRoleFilter customRoleFilter() { return new CustomRoleFilter(); } }
我尝试在自定义过滤器中添加角色,但未生效:
public class CustomRoleFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // TODO:: Extract roles from JWT token (cognito user:groups claim). List<String> roles = Arrays.asList("USER", "ADMIN"); // Create authorities from roles. List<SimpleGrantedAuthority> authorities = roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); // Create an Authentication object with the authorities. Authentication authentication = new UsernamePasswordAuthenticationToken( new User("username", "password", authorities), null, authorities ); // Set the Authentication object in the SecurityContextHolder. SecurityContextHolder.getContext().setAuthentication(authentication); // Continue with the filter chain. filterChain.doFilter(request, response); } }
我还尝试在JWT解码器中添加角色,同样无效(我不确定这是否是正确的处理位置):
public class JWTDecoder implements JwtDecoder { @Override public Jwt decode(String token) throws JwtException { try { // https://cognito-idp.region.amazonaws.com/user-pool-id/.well-known/jwks.json. //TODO:: verify the JWT token var parsedJwt = JWTParser.parse(token); Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject()); Map<String, Object> claims = new LinkedHashMap<>(parsedJwt.getJWTClaimsSet().getClaims()); if (claims.get(JwtClaimNames.IAT) instanceof Date) { var iat = ((Date) claims.get(JwtClaimNames.IAT)).toInstant(); claims.put(JwtClaimNames.IAT, iat); } if (claims.get(JwtClaimNames.EXP) instanceof Date) { var exp = ((Date) claims.get(JwtClaimNames.EXP)).toInstant(); claims.put(JwtClaimNames.EXP, exp); } //TODO:: does not work either claims.put("roles", List.of("USER", "ADMIN").toArray()); return Jwt.withTokenValue(parsedJwt.getParsedString()) .headers(h -> h.putAll(headers)) .claims(c -> c.putAll(claims)) .build(); } catch (ParseException e) { throw new JwtException("Provided token is not valid"); } catch (Exception ex) { throw new JwtException("Error during parse token"); } } }
JWT负载中的用户组示例(应映射为角色):
"cognito:groups": [ "USER" ]
我尝试过添加前置过滤器从Cognito JWT的cognito:groups声明中提取角色,也尝试过在JWTDecoder中提取角色,还试过在认证管理器中处理,但都未能解决问题。
解决方案
核心问题是自定义过滤器和手动解码器干扰了Spring Security内置的OAuth2认证流程,且未正确映射Cognito的角色声明。以下是正确实现方式:
1. 移除自定义过滤器与手动JWTDecoder
Spring Security OAuth2资源服务器模块已内置Cognito JWT处理能力,手动实现会覆盖内置认证逻辑,导致角色无法被正确识别。
2. 配置内置JWT解码器
使用JwtDecoders工具类自动配置解码器,它会自动处理JWT签名验证、过期检查等:
@Bean public JwtDecoder jwtDecoder() { String issuerUri = "https://cognito-idp.{region}.amazonaws.com/{user-pool-id}"; return JwtDecoders.fromIssuerLocation(issuerUri); }
3. 配置JWT权限转换器
实现JwtAuthenticationConverter,将Cognito的cognito:groups声明转换为Spring Security的GrantedAuthority:
@Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); // 匹配hasRole("USER")的自动前缀要求 grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); // 指定从cognito:groups提取角色 grantedAuthoritiesConverter.setAuthoritiesClaimName("cognito:groups"); JwtAuthenticationConverter authenticationConverter = new JwtAuthenticationConverter(); authenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); return authenticationConverter; }
4. 更新SecurityFilterChain配置
整合解码器与转换器,移除自定义过滤器:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http, JwtDecoder jwtDecoder, JwtAuthenticationConverter jwtAuthenticationConverter) throws Exception { return http .authorizeHttpRequests(auth -> { auth.requestMatchers("/api/v1/products").permitAll(); auth.requestMatchers("/api/v1/products/**").hasRole("USER"); auth.anyRequest().authenticated(); }) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .decoder(jwtDecoder) .jwtAuthenticationConverter(jwtAuthenticationConverter) ) ) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .build(); } @Bean public JwtDecoder jwtDecoder() { String issuerUri = "https://cognito-idp.{region}.amazonaws.com/{user-pool-id}"; return JwtDecoders.fromIssuerLocation(issuerUri); } @Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); grantedAuthoritiesConverter.setAuthoritiesClaimName("cognito:groups"); JwtAuthenticationConverter authenticationConverter = new JwtAuthenticationConverter(); authenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); return authenticationConverter; } }
关键说明
hasRole("USER")会自动检查权限是否为ROLE_USER,因此转换器需添加ROLE_前缀- 内置解码器会自动处理JWKS验证、过期时间校验等,无需手动解析JWT
- 自定义前置过滤器会在内置认证流程前执行,覆盖了Spring Security的JWT认证逻辑,导致角色无法被正确识别
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

