You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 19:53:14