PreAuthorize not working on Controller

50,287

Solution 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

Solution 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 {

Solution 3

put @EnableGlobalMethodSecurity(prePostEnabled = true) into MvcConfig class (extends WebMvcConfigurerAdapter) instead of (extends WebSecurityConfigurerAdapter).

Like below example:-

@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MvcConfiguration extends WebMvcConfigurerAdapter {

Solution 4

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

Solution 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);
}
Share:
50,287
prettyvoid
Author by

prettyvoid

ifelse

Updated on August 19, 2021

Comments

  • prettyvoid
    prettyvoid over 2 years

    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.

  • prettyvoid
    prettyvoid over 8 years
    Thanks for the answer. I've tried using the annotation on a CrudRepository interface and it worked fine. Having an interface to each controller is kinda silly in my opinion as an interface isn't really necessary. proxy-target-class=true didn't make a difference, the annotations still doesn't work on controllers, however it caused a crash (while set to true) when having the annotation inside the repository interface (Cannot subclass com.sun.proxy). Anyway I think I'm going to stick to rules in the security config, and maybe use PreAuth on some of my repositories.
  • prettyvoid
    prettyvoid over 8 years
    One other thing that I noticed, I've seen examples where the @PreAuthorize was used in a class instead of an interface and it's supposed to be working.. I wonder why it works for them but not for me.
  • Rudolf Schmidt
    Rudolf Schmidt about 8 years
    I had the same issue and making the methods public solved it!
  • Mradul Pandey
    Mradul Pandey almost 8 years
    Enable Aop in application context <aop:aspectj-autoproxy />
  • Kieveli
    Kieveli almost 8 years
    Adding the interface fixed it for me, but another project doesn't have interfaces, and the controller methods are protected. There must be some other configuration that they're using that I'm not.
  • An Nguyen
    An Nguyen almost 8 years
    I am facing the same issue but opposite. @PreAuthorize works normal on controller layer but not on service layer. Do you know why?
  • Piyush
    Piyush over 6 years
    making the controller methods public solved it for me.
  • valik
    valik over 6 years
    and this source should be added where and how does my controller access it ?
  • valik
    valik over 6 years
    mine worked without this @EnableAspectJAutoProxy I just used @PreAuthorize("hasAuthority('ADMIN')")
  • valik
    valik over 6 years
    why is that? @kripal kashyav
  • Dherik
    Dherik over 6 years
    I had weird problems using @PreAuthorize on Controller: some applications works well and some applications don't.
  • GameSalutes
    GameSalutes almost 6 years
    Also per previous commenter if putting pre/post on a controller method that does not implement an interface also add annotation property proxyTargetClass = true to EnableGlobalMethodSecurity
  • Stefan Falk
    Stefan Falk over 5 years
    Does not work for me unfortunately (see stackoverflow.com/questions/53125388/…).
  • beautifulcode
    beautifulcode over 5 years
    The problem of the ExampleController, only changed ROLE_ADMIN to ADMIN
  • Krzysztof Skrzynecki
    Krzysztof Skrzynecki over 4 years
    OP did it already using @EnableGlobalMethodSecurity(prePostEnabled = true)
  • Arshad Ali
    Arshad Ali about 4 years
    I tried as you suggested it works fine, but what is the correct way of implementing hasRole clause like @PreAuthorize("hasRole('ADMIN')")
  • Wecherowski
    Wecherowski over 3 years
    has this been changed by now? Because the prePost annotations work absolutely fine on my controllers without implementing any interface or allowing target class proxying
  • AreUMinee
    AreUMinee over 3 years
    Changing the method from private to public worked for me.
  • anavaras lamurep
    anavaras lamurep almost 3 years
    i 'm trying this for past one month , everything is correct , expect this public one ..hell of a learning. Finally found the problem. thanks @georgi Peev
  • Stanislau Listratsenka
    Stanislau Listratsenka almost 2 years
    it worked for me, but can't image why