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

Spring Security角色权限配置后requestMatchers始终返回403禁止访问

OAuth2资源服务器JWT 403权限问题排查与解决

核心问题分析

你的代码里存在多个导致403的关键点,逐一拆解:

1. JWT签发时间配置错误

TokenService中把issuedAt设为当前时间加1小时,这会导致令牌的签发时间晚于当前时间,令牌处于未生效状态,直接被Spring Security判定为无效,返回403。

2. hasAnyRole方法使用错误

Spring Security的hasRole()/hasAnyRole()会自动为传入的角色名添加ROLE_前缀。你传入"ROLE_USER",实际会被解析为ROLE_ROLE_USER,与令牌中的角色不匹配。

3. JWT权限解析逻辑不匹配

Spring Security默认会把JWT中scope字段的内容处理为scope权限(自动添加SCOPE_前缀),但你存入的是ROLE_开头的角色,默认解析逻辑无法识别这些角色为合法权限。

4. 冗余配置干扰资源服务器逻辑

你在资源服务器配置中添加了userDetailsService和httpBasic,但OAuth2资源服务器(JWT模式)是自包含令牌认证,不需要加载用户详情或HttpBasic认证,这些配置可能导致认证逻辑冲突。


解决步骤

步骤1:修复JWT签发时间与过期时间

修改TokenService中的JWT生成逻辑,正确设置签发时间和过期时间:

public String generateToken(Authentication authentication){
    Instant now = Instant.now();

    String scope = authentication.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            .collect(Collectors.joining(" "));

    JwtClaimsSet claims =  JwtClaimsSet.builder()
            .issuer("self")
            .issuedAt(now) // 改为当前时间,确保令牌立即生效
            .expiresAt(now.plus(1 , ChronoUnit.HOURS)) // 添加正确的过期时间
            .subject(authentication.getName())
            .claim("scope" , scope)
            .build();

    return this.jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
}

步骤2:修正Security权限配置

改用hasAnyAuthority()方法(不会自动添加前缀),并简化重复的路由规则:

public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, JwtAuthenticationConverter jwtAuthenticationConverter) throws Exception{
    return httpSecurity
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(a ->
            {
                // 合并/student/和/student/**规则
                a.requestMatchers("/student/**").hasAnyAuthority("ROLE_USER");
                // 合并/students/和/students/**规则
                a.requestMatchers("/students/**").hasAnyAuthority("ROLE_ADMIN");
                // 简化公开路由配置
                a.requestMatchers("/", "/token/**").permitAll();
                a.anyRequest().authenticated();
            })
            .oauth2ResourceServer(oauth2 -> oauth2
                    .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter))
            )
            .headers(headers -> headers.frameOptions().sameOrigin())
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .build();
}

步骤3:自定义JWT权限解析转换器

添加自定义JwtAuthenticationConverter,让Spring Security直接解析scope字段中的角色为GrantedAuthority:

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
    converter.setJwtGrantedAuthoritiesConverter(jwt -> {
        // 从JWT的scope字段中获取权限列表
        String scope = jwt.getClaimAsString("scope");
        Collection<String> authorities = scope != null ? Arrays.asList(scope.split(" ")) : Collections.emptyList();
        
        return authorities.stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
    });
    return converter;
}

步骤4:移除冗余配置

删除Security配置中的userDetailsService(userDetailsService)和httpBasic(Customizer.withDefaults()),避免干扰JWT认证逻辑。


验证要点

  1. 生成令牌后,通过JWT解析工具检查:
    • iat字段为当前时间戳
    • exp字段为当前时间加1小时的时间戳
    • scope字段包含正确的角色(如ROLE_USER或ROLE_ADMIN)
  2. 用对应角色的令牌访问路由,确认权限控制生效。

内容的提问来源于stack exchange,提问作者Expert_Introvert

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 15:19:32