I'm trying to define access rules at method-level but it's not working what so ever.
SecurityConfiguration
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().
withUser("user").password("user").roles("USER").and().
withUser("admin").password("admin").roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/v2/**").authenticated()
.and()
.httpBasic()
.realmName("Secure api")
.and()
.csrf()
.disable();
}
}
ExampleController
@EnableAutoConfiguration
@RestController
@RequestMapping({"/v2/"})
public class ExampleController {
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@RequestMapping(value = "/home", method = RequestMethod.GET)
String home() {
return "Hello World";
}
}
Whenever I try to access /v2/home using user:user
it executes just fine, shouldn't it give me an Access Denied error due to 'user' not having ROLE_ADMIN
?
I'm actually thinking of ditching access rules at method-level and stick to http() ant rules, but I have to know why it's not working for me.
A common problem with using PrePost annotations on controllers is that Spring method security is based on Spring AOP, which is by default implemented with JDK proxies.
That means that it works fine on the service layer which is injected in controller layer as interfaces, but it is ignored on controller layer because controller generally do not implement interfaces.
The following is just my opinion:
- prefered way: move the pre post annotation on service layer
- if you cannot (or do not want to), try to have your controller implement an interface containing all the annotated methods
- as a last way, use proxy-target-class=true
You have to add @EnableGlobalMethodSecurity(prePostEnabled = true)
in your WebSecurityConfig.
You can find it here: http://www.baeldung.com/spring-security-expressions-basic
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
I had a similar problem and the following solved it:
1) I had to make my method public (i.e. make your method home() public)
2) I have to use hasRole instead of hasAuthority
For making it working on controller layer.
I had to put @EnableAspectJAutoProxy on my configuration class.
Example :
@Configuration
@EnableWebMvc
@EnableAspectJAutoProxy
@ComponentScan(basePackages = { "com.abc.fraud.ts.userservices.web.controller" })
public class WebConfig extends WebMvcConfigurerAdapter{
}
There are two different ways to use this, one is to prefix and one is not.
And you maybe change @PreAuthorize("hasAuthority('ROLE_ADMIN')")
to @PreAuthorize("hasAuthority('ADMIN')")
will be ok.
next is @PreAuthorize
source code.
private String defaultRolePrefix = "ROLE_";
public final boolean hasAuthority(String authority) {
return hasAnyAuthority(authority);
}
public final boolean hasAnyAuthority(String... authorities) {
return hasAnyAuthorityName(null, authorities);
}
public final boolean hasRole(String role) {
return hasAnyRole(role);
}
public final boolean hasAnyRole(String... roles) {
return hasAnyAuthorityName(defaultRolePrefix, roles);
}
If you have a xml context file for your security beans and a separate one for your web/servlet context, than you als need to add:
<security:global-method-security pre-post-annotations="enabled"/>
to your web-context.xml / servlet context. Its not enough to just add it in the security context xml.
Its not inherited in child contexts.
HTH