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

Spring Boot集成Spring Security接口请求返回403 Forbidden问题排查

Spring Security 接口返回403问题排查

问题现象

开发Spring Boot应用集成Spring Security组件时,通过Postman发起接口请求始终返回403 Forbidden错误:

  • 已在安全配置中添加.csrf().disable()配置,问题未解决
  • 将匹配/person/**路径的接口配置为permitAll()放行后,接口可正常响应

项目相关代码

User实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("User")
public class User {
    @Id
    private String id;
    private String name;
    private String password;
    private String email;
    private Set<UserRole> roles;
}

安全配置类

public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final UserDetailsService userDetailsService;
    private final BCryptPasswordEncoder encoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        log.info("HttpSecurity: {}",http);
        http.authorizeRequests()
                .antMatchers( "/user/saveUser").permitAll()
                .antMatchers("/person/**").hasAnyRole()
                .and().csrf().disable().cors().disable();
    }
}

UserDetailsService实现

public class UserService implements UserDetailsService{
    private final UserRepository userRepo;
    private final BCryptPasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        log.info("mail request: {}",email);
        Optional<User> opt = userRepo.findUserByEmail(email);
        log.info("Find user: {}", opt);
        org.springframework.security.core.userdetails.User springUser=null;

        if(opt.isEmpty()) {
            throw new UsernameNotFoundException("User with email: " +email +" not found");
        }else {
            User user =opt.get();
            Set<UserRole> roles = user.getRoles();
            Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
            for(UserRole role:roles) {
                grantedAuthorities.add(new SimpleGrantedAuthority(role.name()));
            }
            springUser = new org.springframework.security.core.userdetails.User(
                    email,
                    user.getPassword(),
                    grantedAuthorities );
        }
        return springUser;
    }
}

User控制器

@RestController
@RequestMapping("user")
public class UserController {
    private final UserService userService;
    @PostMapping("/saveUser")
    public ResponseEntity<String> saveUser(@RequestBody User user) {
        log.info("Registering User: {}", user);
        userService.saveUser(user);
        return ResponseEntity.ok("registered User");
    }
}

Person控制器(触发403的接口)

@RestController
@RequestMapping("person")
public class PersonController {
    @Autowired
    PersonService personService;

    @GetMapping("/getAll")
    public ResponseEntity<List> getAll() throws IOException {
        return ResponseEntity.ok(PersonService.findAll());
    }
}

问题根因

一共3个核心配置错误,直接导致接口被拦截返回403:

  1. hasAnyRole()未传参数:该方法如果不传入任何角色值,相当于没有任何角色拥有访问权限,所有匹配路径的请求都会被直接拒绝。
  2. 角色前缀规则不匹配:Spring Security的hasRole()/hasAnyRole()方法校验权限时,默认会给传入的角色名拼接ROLE_前缀再和用户持有的权限比对,但你在UserService中组装权限时直接存入了枚举的原始name(比如USER、ADMIN),和框架要求的ROLE_USER、ROLE_ADMIN格式完全不匹配,就算传了角色参数也会校验失败。
  3. 未开启认证方式支持:当前配置没有开启HTTP Basic或表单登录,Postman发起请求时没有携带合法认证凭证,请求根本无法通过身份校验环节。

修复方案

按以下步骤修改配置即可解决问题:

  1. 给hasAnyRole()传入允许访问的角色值,比如允许普通用户和管理员访问就写hasAnyRole("USER", "ADMIN")。
  2. 统一角色前缀规则,二选一即可:
    • 方案1:组装权限时手动拼接前缀,将new SimpleGrantedAuthority(role.name())改为new SimpleGrantedAuthority("ROLE_" + role.name()),适配hasRole/hasAnyRole的默认规则
    • 方案2:将hasAnyRole()替换为hasAnyAuthority()方法,该方法不会自动拼接前缀,直接和你存入的原始权限值匹配,写法为hasAnyAuthority("USER", "ADMIN")
  3. 在HttpSecurity配置中添加.httpBasic()开启HTTP Basic认证支持,方便Postman测试时直接在Authorization标签页选择Basic Auth填写用户名(邮箱)和密码即可发起认证请求。
  4. 补充兜底权限规则,在权限匹配链最后添加.anyRequest().authenticated(),表示除了明确放行的路径外,其余所有接口都需要认证后才能访问,避免出现未匹配路径的权限异常。

修正后的HttpSecurity配置参考

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers( "/user/saveUser").permitAll()
            // 示例用hasAnyAuthority,和你当前的权限存储逻辑匹配
            .antMatchers("/person/**").hasAnyAuthority("USER", "ADMIN")
            .anyRequest().authenticated()
            .and()
            .httpBasic() // 开启Basic认证支持Postman测试
            .and()
            .csrf().disable()
            .cors().disable();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 14:01:12