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

Spring Security已认证用户访问无权限资源返回401而非403的修复

问题修复:Admin用户访问User端点返回401而非403的解决方法

问题描述

我定义了两个受保护端点forAdmin()和forUser(),分别供Admin和User角色访问。已认证的Admin能正常访问forAdmin(),User能正常访问forUser(),但Admin用户尝试访问forUser()时,返回的是401 Unauthorized(未认证),而非预期的403 Access Denied(权限不足)。

问题原因

核心问题在于Spring Security授权规则的配置顺序和异常处理逻辑的触发条件:

  1. 原配置中authorizeHttpRequests被调用两次,第二次的anyRequest().authenticated()会干扰前序规则的执行优先级,导致角色校验异常被误判为未认证。
  2. 若UserDetails中角色格式不符合Spring Security的hasRole规则(缺少ROLE_前缀),也会导致权限校验失败,进而触发错误的异常类型。

修复步骤

1. 合并授权规则配置

将分散的authorizeHttpRequests调用合并为一次,确保规则按优先级顺序执行:先放行公开接口,再匹配角色专属接口,最后兜底所有请求需认证。

修改后的WebSecurityConfiguration核心代码:

@Configuration
//@EnableMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class WebSecurityConfiguration {

    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private JwtRequestFilter jwtRequestFilter;
    private CustomUserDetailsService jwtService;
    private static final String[] ALLOWED_ACCESS = {"/authenticate", "/registerNewUser"};

    @Autowired
    public WebSecurityConfiguration(JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
            JwtRequestFilter jwtRequestFilter, CustomUserDetailsService jwtService) {
        this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
        this.jwtRequestFilter = jwtRequestFilter;
        this.jwtService = jwtService;
    }

    @Bean
    AuthenticationManager authenticationManagerBean(AuthenticationConfiguration authenticationConfiguration)
            throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
            .cors(cors -> cors.disable()) // 根据实际需求调整CORS配置
            .authorizeHttpRequests(authz -> authz
                .requestMatchers(ALLOWED_ACCESS).permitAll()
                .requestMatchers("/forAdmin").hasRole("Admin")
                .requestMatchers("/forUser").hasRole("User")
                .anyRequest().authenticated()
            )
            .exceptionHandling(exceptionHandling -> exceptionHandling
                .accessDeniedHandler(new CustomAccessDeniedHandler())
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
            )
            .sessionManagement(sessionManagement -> sessionManagement
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
    
    public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
         authenticationManagerBuilder.userDetailsService(jwtService).passwordEncoder(passwordEncoder());
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

2. 确保角色格式符合Spring Security要求

Spring Security的hasRole方法会自动为角色添加ROLE_前缀,因此在CustomUserDetailsService中,需将数据库中的角色转换为带前缀的GrantedAuthority:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findByUsername(username);
    if (user == null) {
        throw new UsernameNotFoundException("User not found with username: " + username);
    }
    // 为角色添加ROLE_前缀,适配hasRole规则
    List<GrantedAuthority> authorities = user.getRoles().stream()
        .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
        .collect(Collectors.toList());
    return new org.springframework.security.core.userdetails.User(
        user.getUsername(),
        user.getPassword(),
        authorities
    );
}

修复原理

  • 合并授权规则后,Admin用户访问/forUser时会优先匹配hasRole("User")规则,触发权限不足的AccessDeniedException,进而被CustomAccessDeniedHandler捕获,返回403状态码。
  • 修正角色格式后,hasRole能正确匹配用户权限,避免因角色不匹配导致的异常误判。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 04:37:03