Spring Boot 安全中的 HTTP 403 禁止错误

Kar*_*hik 7 spring spring-security basic-authentication spring-boot spring-security-rest

Spring安全配置类

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private UserDetailsService userDetailsService;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .cors()
            .and()
            .authorizeRequests()
            .antMatchers("/user", "/login").permitAll()
            .antMatchers("/employee", "/insurance").hasRole("User")
            .anyRequest()
            .authenticated()
            .and()
            .httpBasic()
            .and()
            .csrf().disable();
    }

    protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
    }
}
Run Code Online (Sandbox Code Playgroud)

UserDetailsS​​ervice 实现类

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        User user = null;
        Set<GrantedAuthority> grantedAuthorities = null;
        try
        {
            user = userService.findByUserName(userName);
            if(user == null)
                throw new UsernameNotFoundException("User " + userName  + " not available");

            grantedAuthorities = new HashSet<>();
            for(Role role: user.getRoles()) {
                grantedAuthorities.add(new SimpleGrantedAuthority(role.getRole().toString()));
            }
        }
        catch(Exception exp) {
            exp.printStackTrace();
        }
        return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), grantedAuthorities);
    }
}
Run Code Online (Sandbox Code Playgroud)

员工休息控制器类

@RestController
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    @Autowired
    private InsuranceService insuranceService;

    @PostMapping("/employee")
    public ResponseEntity<Employee> create(@RequestBody Employee employee) throws Exception {
        employee = employeeService.create(employee);
        return new ResponseEntity<Employee>(employee, HttpStatus.CREATED);
    }

    @PutMapping("/employee")
    public ResponseEntity<Employee> update(@RequestBody Employee employee) throws Exception {
        employee = employeeService.update(employee);
        return new ResponseEntity<Employee>(employee, HttpStatus.OK);
    }

    @DeleteMapping("/employee/{id}")
    public ResponseEntity<String> delete(@PathVariable("id") long id) throws Exception {
        employeeService.delete(id);
        return new ResponseEntity<String>("Employee deleted successfully", HttpStatus.OK);
    }

    @GetMapping("/employee/{id}")
    public ResponseEntity<Employee> findEmployeeDetails(@PathVariable("id") long id) throws Exception {
        Employee employee = employeeService.findById(id);
        return new ResponseEntity<Employee>(employee, HttpStatus.OK);
    }

    @GetMapping("/employee")
    public ResponseEntity<List<Employee>> findAll() throws Exception {
        List<Employee> employees = employeeService.findAll();
        return new ResponseEntity<List<Employee>>(employees, HttpStatus.OK);
    }
}
Run Code Online (Sandbox Code Playgroud)

对于通过邮递员提交到/employee URL 的任何 HTTP 方法(POST/GET/PUT)请求,我收到 403 禁止错误

{
    "timestamp": "2019-09-17T05:37:35.778+0000",
    "status": 403,
    "error": "Forbidden",
    "message": "Forbidden",
    "path": "/hr-core/employee"
}
Run Code Online (Sandbox Code Playgroud)

即使我在 POSTMAN 中 HTTP 请求的基本身份验证标头(授权)中发送正确的用户名和密码,我也收到此错误。此用户还具有 USER 和 ADMIN 角色来访问/employee REST 端点。我在 http 安全中禁用了CSRF

我该如何解决这个错误?

g00*_*00b 13

在 Spring Security 中,角色权限之间存在差异。虽然权限可以是任何东西,但角色是以 开头的权限子集ROLE_

假设您拥有以下权限:

GrantedAuthority authority1 = new SimpleGrantedAuthority("User");
GrantedAuthority authority2 = new SimpleGrantedAuthority("ROLE_Admin");
Run Code Online (Sandbox Code Playgroud)

在本例中,authority1不包含角色,而包含角色,authority2因为它带有前缀ROLE_

这意味着,如果您使用hasRole("User"),您将无权访问,因为它未定义为角色。hasRole("Admin")另一方面会起作用。

要解决此问题,您有两种选择:

  1. 确保您的角色确实以ROLE_. 如果您不以这种方式将它们存储在您的数据库中,您可以修改您的UserDetailsServiceImpl

    String roleName = "ROLE_" + role.getRole().toString();
    grantedAuthorities.add(new SimpleGrantedAuthority(roleName));
    
    Run Code Online (Sandbox Code Playgroud)
  2. 或者,您可以hasAuthority("User")改用:

    // ...
    .antMatchers("/employee", "/insurance").hasAuthority("User")
    // ...
    
    Run Code Online (Sandbox Code Playgroud)