PreAuthorize not working on Controller

2019-01-18 04:42发布

问题:

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.

回答1:

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


回答2:

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 {


回答3:

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



回答4:

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{

}


回答5:

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);
}


回答6:

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