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

Spring Security hasAnyRole失效,请求返回403 Forbidden问题求助

解决Spring Security中.hasAnyRole规则失效问题

问题现象

  • 已注册TRAINER角色的用户,通过POST请求http://localhost:8082/sign/in成功获取JWT令牌
  • 携带该Bearer令牌发送GET请求http://localhost:8082/trainer/test时,返回403 Forbidden状态码

控制台日志

o.s.s.w.access.AccessDeniedHandlerImpl [0;39m [2m:[0;39m Responding with 403 status code
o.s.security.web.FilterChainProxy       [0;39m [2m:[0;39m Securing GET /error
o.s.s.w.a.AnonymousAuthenticationFilter [0;39m [2m:[0;39m Set SecurityContextHolder to anonymous SecurityContext
o.s.s.w.a.Http403ForbiddenEntryPoint    [0;39m [2m:[0;39m Pre-authenticated entry point called. Rejecting access

核心代码片段

SecurityConfiguration类

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {

    private final JwtAuthenticationFilter jwtAuthFilter;
    
    private final AuthenticationProvider authenticationProvider;
    
    private final LogoutHandler logoutHandler;
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf()
            .disable()
            .authorizeHttpRequests()
            .requestMatchers("/sign/**")
            .permitAll()
            .requestMatchers("/admin/**").hasAnyRole(Role.ADMIN.name())
            .requestMatchers("/trainer/**").hasAnyRole(Role.TRAINER.name())
            .anyRequest()
            .authenticated()
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authenticationProvider(authenticationProvider)
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
            .logout()
            .logoutUrl("/logout")
            .addLogoutHandler(logoutHandler)
            .logoutSuccessHandler(
                    (request, response, authentication) -> 
                    SecurityContextHolder.clearContext());
            
        return http.build();
    }
}

JwtService类(关键部分)

@Service
public class JwtService {
    // ...其他代码
    
    public String generateToken(UserDetailsImpl userDetails) {
        Map<String, Object> extraClaims = new HashMap<>();
        extraClaims.put("id :", userService.getRoleIdFromUserId(userDetails.getId()));
        extraClaims.put("role", userDetails.getAuthorities().stream().findFirst().get().getAuthority());
        return generateToken(extraClaims, userDetails);
    }
    
    // ...其他代码
}

Role枚举

public enum Role {
    ADMIN,
    TRAINER
}

问题根源

Spring Security的hasAnyRole()方法会自动为角色名称添加ROLE_前缀进行匹配,但当前代码中:

  1. Role枚举定义的是ADMIN/TRAINER,无ROLE_前缀
  2. UserDetailsImpl的getAuthorities()返回的权限(或JWT中存储的角色)也没有ROLE_前缀
  3. 权限校验时,系统期望匹配ROLE_TRAINER,但实际用户持有的是TRAINER,导致匹配失败返回403

解决方案

提供两种可行方案,任选其一即可:

方案1:统一添加ROLE_前缀

方式A:修改Role枚举

直接在枚举值中加入ROLE_前缀:

public enum Role {
    ROLE_ADMIN,
    ROLE_TRAINER
}

方式B:在UserDetailsImpl中动态添加前缀

如果不想修改枚举,可在getAuthorities()方法中为角色添加前缀:

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    // 假设user是当前用户实体,role字段为Role枚举
    return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + user.getRole().name()));
}

同时确保JwtAuthenticationFilter解析JWT后,构造Authentication对象时使用带前缀的权限:

// JwtAuthenticationFilter的doFilterInternal方法中
String role = jwtService.extractClaim(token, claims -> claims.get("role", String.class));
// 如果用hasRole,这里要确保权限带ROLE_前缀
Collection<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority(role));

UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
        userDetails,
        null,
        authorities
);
SecurityContextHolder.getContext().setAuthentication(authToken);

方案2:改用hasAnyAuthority()替代hasAnyRole()

hasAnyAuthority()不会自动添加前缀,直接使用原始角色名称匹配,修改SecurityConfiguration中的规则:

.requestMatchers("/admin/**").hasAnyAuthority(Role.ADMIN.name())
.requestMatchers("/trainer/**").hasAnyAuthority(Role.TRAINER.name())

此方案无需修改角色枚举或UserDetailsImpl,只需确保JWT中存储的角色和用户持有的权限与枚举名称一致即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 19:45:01