问题
I'm trying to accomplish annotation-based (using @PreAuthorize) permission checking on one of my controllers. I'm using Java config, and have a SecurityConfig class extending WebSecurityConfigurerAdapter.
I'm also using Basic HTTP Authentication to get to the methods that require authorization.
But - the problem is, I don't want the SecurityConfig determining which methods require authentication using an antPattern() or similar. That's what the annotations are for!
How can I configure my SecurityConfig class so that it properly uses HTTP Basic authentication and the configured AuthenticationManager for methods with @PreAuthorize, and allows anonymous access to controller methods that do not have any security annotations attached?
When I try this (Attempt #1):
@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
@Autowired
public void registerGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("craig").password("craigpass").roles("USER");
}
}
My unprotected methods fail with:
Full authentication is required to access this resource
And my protected (but properly-authenticating) tests fail with:
Error: Expected CSRF token not found. Has your session expired?
When I try this (Attempt #2):
@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void registerGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("craig").password("craigpass").roles("USER");
}
}
My unprotected methods fail with a 302 status code, and redirect to /login, and my protected/authenticating methods fail with:
Error: Expected CSRF token not found. Has your session expired?
When I try this (Attempt #3):
@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
@Autowired
public void registerGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("craig").password("craigpass").roles("USER");
}
}
My authenticating method works properly. (Woo hoo!) But, my unprotected method fails with 401 because my HttpSecurity has seemingly been configured to only allow access if you're authenticated - regardless of whether the controller method is annotated with @PreAuthorize.
When I try this (Attempt #4):
@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}
@Autowired
public void registerGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("craig").password("craigpass").roles("USER");
}
}
My unprotected methods work properly (allow access), but my authenticating methods (whether they have the proper HTTP auth or not) fail with status code 403:
Error: Access Denied
My authenticating test method that fails with 403 uses MockMvc and sends in a request using headers:
{Content-Type=[application/json], Accept=[application/json], Authorization=[Basic Y3JhaWc6Y3JhaWdwYXNz]}
Which I have decoded and verified, and a URL /api/item with method POST, which should target the corresponding method:
@RequestMapping(value = "/api/item", method = { RequestMethod.POST })
@PreAuthorize("hasRole('ROLE_USER')")
public void postNewItem(HttpServletRequest request, HttpServletResponse response) {
...
}
回答1:
I've done something similar the only difference is that i had some very customized authentication method. If the only thing you want is to check for user role you should use @Secured instead of @PreAuthorize on your controller methods e.g.
@Secured({"ROLE_SOMEROLE"})
public void doSomething(...) {
}
Where the role is set in AuthenticationProvider as a list of SimpleGrantedAuthority with UsernamePasswordAuthenticationToken.
Here is the configuration class:
@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(securedEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationProvider authProvider;
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// Had some other calls for CORS ajax requests.
http.authorizeRequests()
.antMatchers("/logout")
.permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.csrf()
.disable();
}
}
If your methods are annotated with @Secured and the current user does not have specified role the client will receive a 403 response else the request goes through. If you are trying to run unit testing you can use @Profile('test') on your beans and use AuthenticationProvider with hard coded data. Here a post on this type of approach:
https://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/
来源:https://stackoverflow.com/questions/26128826/using-java-config-with-spring-security-for-annotation-based-role-checks-and-basi