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

调用接口返回403 Forbidden,自定义认证Provider问题排查求助

排查接口返回403 Forbidden的问题

调用任意接口均返回403 Forbidden错误,为实现基于角色的认证,编写了CustomAuthenticationProvider及SecurityConfig配置类,相关代码如下:

CustomAuthenticationProvider 代码

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

private final CustomerRepository customerRepository;
private final PasswordEncoder passwordEncoder;

public CustomAuthenticationProvider(CustomerRepository customerRepository, PasswordEncoder passwordEncoder) {
    this.customerRepository = customerRepository;
    this.passwordEncoder = passwordEncoder;
}

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {

    Customer customer = customerRepository.findByEmail(authentication.getName()).orElseThrow(() -> new CustomerNotFoundException(authentication.getName()));
    if(passwordEncoder.matches(authentication.getCredentials().toString(), customer.getPwd())) {
        return new UsernamePasswordAuthenticationToken(
                customer.getEmail(), customer.getPwd(), getGrantedAuthorities(customer.getAuthorities()));
    }
    else{
        throw new BadCredentialsException("Invalid Credentials");
    }
}

private List<GrantedAuthority> getGrantedAuthorities(Set<Authority> authorities) {
    List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
    for (Authority authority : authorities) {
        grantedAuthorities.add(new SimpleGrantedAuthority(authority.getName()));
    }
    return grantedAuthorities;
}

@Override
public boolean supports(Class<?> authentication) {
    return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}

SecurityConfig 代码

@Configuration
public class SecurityConfig {

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {

    CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
    requestHandler.setCsrfRequestAttributeName("_csrf");
    http.securityContext((context) -> context.requireExplicitSave(false))
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.ALWAYS))
            .cors(cors -> cors.configurationSource(new CorsConfigurationSource() {
                @Override
                public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                    CorsConfiguration config = new CorsConfiguration();
                    config.setAllowedOrigins(Collections.singletonList("http://localhost:4200"));
                    config.setAllowedMethods(Collections.singletonList("*"));
                    config.setAllowCredentials(true);
                    config.setAllowedHeaders(Collections.singletonList("*"));
                    config.setMaxAge(3600L);
                    return config;
                }
            })).csrf((csrf) -> csrf
                    .csrfTokenRequestHandler(requestHandler).ignoringRequestMatchers("/contact","/register")
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
            .addFilterAfter(new CSRFCookieFilter(), BasicAuthenticationFilter.class)
            .authorizeHttpRequests((requests)->requests
                    .requestMatchers("/myAccount").hasRole("USER")
                    .requestMatchers("/myBalance").hasAnyRole("USER","ADMIN")
                    .requestMatchers("/myLoans").hasRole("USER")
                    .requestMatchers("/myCards").hasRole("USER")
                    .requestMatchers("/user").authenticated()
                    .requestMatchers("/notices","/contact","/register").permitAll())
            .formLogin(Customizer.withDefaults())
            .httpBasic(Customizer.withDefaults());
    return http.build();
}

@Bean
public PasswordEncoder getPasswordEncoder(){
    return new BCryptPasswordEncoder();
}
}

问题排查与修复方案

1. 权限前缀不匹配

Spring Security的hasRole()方法会自动为角色名添加ROLE_前缀,但你的getGrantedAuthorities方法直接将数据库中存储的角色名(如USER)作为权限标识,导致实际权限为USER,而hasRole("USER")会校验ROLE_USER,两者不匹配触发403。

修复方式二选一:

  • 生成权限时添加前缀:
    private List<GrantedAuthority> getGrantedAuthorities(Set<Authority> authorities) {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        for (Authority authority : authorities) {
            // 添加ROLE_前缀
            grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + authority.getName()));
        }
        return grantedAuthorities;
    }
    
  • 改用hasAuthority()替代hasRole(),保持权限名一致:
    .authorizeHttpRequests((requests)->requests
            .requestMatchers("/myAccount").hasAuthority("USER")
            .requestMatchers("/myBalance").hasAnyAuthority("USER","ADMIN")
            .requestMatchers("/myLoans").hasAuthority("USER")
            .requestMatchers("/myCards").hasAuthority("USER")
            // 其余规则不变
    )
    

2. 自定义AuthenticationProvider未注册

你的SecurityConfig中没有将CustomAuthenticationProvider注册到Spring Security的认证流程中,导致默认认证逻辑未被替换,认证后可能没有正确赋予角色权限。

修复方式:
在SecurityConfig中注入自定义Provider并配置到HttpSecurity:

@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    // 原有代码...
    // 添加这行注册自定义Provider
    http.authenticationProvider(customAuthenticationProvider);
    // 原有代码...
}

3. 认证Token的Principal信息不完整

当前返回的UsernamePasswordAuthenticationToken使用customer.getEmail()作为principal,若后续业务逻辑需要获取用户的角色或其他信息,可能无法正确获取。建议直接传入Customer对象,让SecurityContext持有完整的用户信息:

修复方式:

return new UsernamePasswordAuthenticationToken(
        customer, // 替换为customer对象
        customer.getPwd(), 
        getGrantedAuthorities(customer.getAuthorities())
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:25:10