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:
hasAnyRole()未传参数:该方法如果不传入任何角色值,相当于没有任何角色拥有访问权限,所有匹配路径的请求都会被直接拒绝。- 角色前缀规则不匹配:Spring Security的
hasRole()/hasAnyRole()方法校验权限时,默认会给传入的角色名拼接ROLE_前缀再和用户持有的权限比对,但你在UserService中组装权限时直接存入了枚举的原始name(比如USER、ADMIN),和框架要求的ROLE_USER、ROLE_ADMIN格式完全不匹配,就算传了角色参数也会校验失败。 - 未开启认证方式支持:当前配置没有开启HTTP Basic或表单登录,Postman发起请求时没有携带合法认证凭证,请求根本无法通过身份校验环节。
修复方案
按以下步骤修改配置即可解决问题:
- 给
hasAnyRole()传入允许访问的角色值,比如允许普通用户和管理员访问就写hasAnyRole("USER", "ADMIN")。 - 统一角色前缀规则,二选一即可:
- 方案1:组装权限时手动拼接前缀,将
new SimpleGrantedAuthority(role.name())改为new SimpleGrantedAuthority("ROLE_" + role.name()),适配hasRole/hasAnyRole的默认规则 - 方案2:将
hasAnyRole()替换为hasAnyAuthority()方法,该方法不会自动拼接前缀,直接和你存入的原始权限值匹配,写法为hasAnyAuthority("USER", "ADMIN")
- 方案1:组装权限时手动拼接前缀,将
- 在HttpSecurity配置中添加
.httpBasic()开启HTTP Basic认证支持,方便Postman测试时直接在Authorization标签页选择Basic Auth填写用户名(邮箱)和密码即可发起认证请求。 - 补充兜底权限规则,在权限匹配链最后添加
.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
相关产品推荐
相关产品推荐

