Spring MVC webapp + REST:授权问题
Spring MVC webapp + REST: Authorization issue
我正在使用 Spring MVC 构建 CRM 系统。现在我正在为系统添加 REST 支持,但是 Spring 安全出于某种原因将允许具有角色 "EMPLOYEE" 的未授权用户访问 POST 方法(在系统)。
一切都适用于表单和身份验证。只是由于某种原因专门针对 RestController 的授权失败。
我正在使用 PostgreSQL 来存储客户和用户以及进行身份验证。
注意:我将“/customer”用于 REST 并作为 webapp 表单的入口点。
这是我的安全配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/customer").hasRole("EMPLOYEE")
.antMatchers(HttpMethod.POST, "/customer").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/customer/showForm*").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/customer/save*").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/customer/delete").hasRole("ADMIN")
.antMatchers("/customer/**").hasRole("EMPLOYEE")
.antMatchers("/resources/**").permitAll()
.and()
.httpBasic()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authenticate")
.permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/access-denied")
.and()
.csrf().disable();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
}
这是我的 RestController:
@RestController
@RequestMapping("/customer")
public class CustomerRestController {
@Autowired
private CustomerService customerService;
@GetMapping
public List<Customer> getCustomers() {
return customerService.getCustomers();
}
@GetMapping("/{customerId}")
public Customer getCustomer(@PathVariable int customerId) {
Customer customer = customerService.getCustomer(customerId);
if (customer == null) throw new CustomerNotFoundException("Customer not found: id = " + customerId);
return customer;
}
@PostMapping
public Customer addCustomer(@RequestBody Customer customer) {
customer.setId(0);
customerService.saveCustomer(customer);
return customer;
}
}
更新:
- 我尝试将 RestController 映射到另一个路径 - 没有用。
- 我还尝试将 SecurityConfig 拆分为多个入口点 - 没有用,并且由于某种原因也开始自动登录。
- 在 RestController 的 Post 方法之前添加了 @Secured({"ROLE_MANAGER", "ROLE_ADMIN"}) - 结果相同。
似乎我的 RestController Spring 根本不关心用户的角色。
经过数小时的研究和反复试验,一切正常。
关键问题是方法路径中缺少星号。而不是“/customer”,它必须是“/customer/**”。
我还稍微更改了 Web 表单和 REST 支持的路径,以避免混淆并使支持更容易。
总之这里是更新的安全配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
@Configuration
@Order(1)
public static class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public AuthenticationEntryPoint entryPoint() {
return new CustomAuthenticationEntryPoint();
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().antMatcher("/api/**")
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/customers/**").hasRole("EMPLOYEE")
.antMatchers(HttpMethod.POST, "/api/customers/**").hasAnyRole("MANAGER", "ADMIN")
.and()
.httpBasic()
.authenticationEntryPoint(entryPoint())
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
@Configuration
@Order(2)
public static class FormSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/customer/showForm*").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/customer/save*").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/customer/delete").hasRole("ADMIN")
.antMatchers("/customer/**").hasRole("EMPLOYEE")
.antMatchers("/resources/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authenticate")
.permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
}
}
我正在使用 Spring MVC 构建 CRM 系统。现在我正在为系统添加 REST 支持,但是 Spring 安全出于某种原因将允许具有角色 "EMPLOYEE" 的未授权用户访问 POST 方法(在系统)。 一切都适用于表单和身份验证。只是由于某种原因专门针对 RestController 的授权失败。
我正在使用 PostgreSQL 来存储客户和用户以及进行身份验证。
注意:我将“/customer”用于 REST 并作为 webapp 表单的入口点。
这是我的安全配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/customer").hasRole("EMPLOYEE")
.antMatchers(HttpMethod.POST, "/customer").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/customer/showForm*").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/customer/save*").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/customer/delete").hasRole("ADMIN")
.antMatchers("/customer/**").hasRole("EMPLOYEE")
.antMatchers("/resources/**").permitAll()
.and()
.httpBasic()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authenticate")
.permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/access-denied")
.and()
.csrf().disable();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
}
这是我的 RestController:
@RestController
@RequestMapping("/customer")
public class CustomerRestController {
@Autowired
private CustomerService customerService;
@GetMapping
public List<Customer> getCustomers() {
return customerService.getCustomers();
}
@GetMapping("/{customerId}")
public Customer getCustomer(@PathVariable int customerId) {
Customer customer = customerService.getCustomer(customerId);
if (customer == null) throw new CustomerNotFoundException("Customer not found: id = " + customerId);
return customer;
}
@PostMapping
public Customer addCustomer(@RequestBody Customer customer) {
customer.setId(0);
customerService.saveCustomer(customer);
return customer;
}
}
更新:
- 我尝试将 RestController 映射到另一个路径 - 没有用。
- 我还尝试将 SecurityConfig 拆分为多个入口点 - 没有用,并且由于某种原因也开始自动登录。
- 在 RestController 的 Post 方法之前添加了 @Secured({"ROLE_MANAGER", "ROLE_ADMIN"}) - 结果相同。
似乎我的 RestController Spring 根本不关心用户的角色。
经过数小时的研究和反复试验,一切正常。 关键问题是方法路径中缺少星号。而不是“/customer”,它必须是“/customer/**”。 我还稍微更改了 Web 表单和 REST 支持的路径,以避免混淆并使支持更容易。 总之这里是更新的安全配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
@Configuration
@Order(1)
public static class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public AuthenticationEntryPoint entryPoint() {
return new CustomAuthenticationEntryPoint();
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().antMatcher("/api/**")
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/customers/**").hasRole("EMPLOYEE")
.antMatchers(HttpMethod.POST, "/api/customers/**").hasAnyRole("MANAGER", "ADMIN")
.and()
.httpBasic()
.authenticationEntryPoint(entryPoint())
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
@Configuration
@Order(2)
public static class FormSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/customer/showForm*").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/customer/save*").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/customer/delete").hasRole("ADMIN")
.antMatchers("/customer/**").hasRole("EMPLOYEE")
.antMatchers("/resources/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authenticate")
.permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
}
}