Spring Security, Method Security annotation (@Secured ) is not working (java config)

40,890

Solution 1

This issue was solved.

I add @EnableGlobalMethodSecurity(prePostEnabled = true)

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter{
}

And in controller i changed @Secured("ADMIN") to @PreAuthorize("hasRole('ADMIN')")

Solution 2

Kindly add this

@EnableGlobalMethodSecurity(securedEnabled = true)

This element is used to enable annotation-based security in your application (by setting the appropriate attributes on the element), and also to group together security pointcut declarations which will be applied across your entire application context specifically for @Secured. Hence your code should look like this

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter{..

Solution 3

I know this thread is quite old and my answer alludes to portions of the answers by various people in this thread; but here is a list combined list of pitfalls and answers:

  1. When using @Secured, and the role name is (e.g.) ADMIN; this means an annotation of @Secured("ROLE_ADMIN").
  2. WebSecurityConfigurerAdapter must have @EnableGlobalMethodSecurity(securedEnabled = true)
  3. As with most Spring related proxies, make sure that the class and the secured methods are not in any way final. For Kotlin this means "open" every method as well as the class.
  4. When the class and its methods are virtual ("open"), then there is no implied need for an interface.

Here is part of a working Kotlin example:

@RestController
@RequestMapping("api/v1")

    open class DiagnosticsController {
        @Autowired
        lateinit var systemDao : SystemDao

        @RequestMapping("ping", method = arrayOf(RequestMethod.GET))
        @Secured("ROLE_ADMIN")
        open fun ping(request : HttpServletRequest, response: HttpServletResponse) : String { ... 
    }

and

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
open class WebSecurityConfig : WebSecurityConfigurerAdapter() {

Regards

Solution 4

There may be many reasons for which method security on a controller does not work.

First because it is never cited as example in Spring Security manual ... joking but it may be tricky to take Spring tools where they do not want to go.

More seriously, you should enable method security as already said by @Mudassar. The manual says :

We can enable annotation-based security using the @EnableGlobalMethodSecurity annotation on any @Configuration instance. For example, the following would enable Spring Security’s @Secured annotation.

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
   // ...
}

Note that Mudassar's answer is correct till here.

But method security is based on AOP, which by default uses JDK proxying on interfaces. That's the reason why all examples applies method security on the service layer, because the service classes are normally injected in controllers as interfaces.

You can of course use it on controller layer, but :

  • either all your controllers implement interfaces for you all @Secured annotated methods
  • or you must switch to class proxying

The rule that I try to follow is :

  • if I want to secure an URL, I stick to HTTPSecurity
  • if I need to allow finer grained access, I add security at service layer

Solution 5

I want to share my decision, may be it will be helpful.

Used spring mvc + spring security, version 4.2.9.RELEASE

For example, i have a Service with method annotated @Secured

@Secured("ACTION_USER_LIST_VIEW")
List<User> getUsersList();

But, it didn't work, because GlobalMethodSecurityConfiguration has inside method.

protected AccessDecisionManager accessDecisionManager()

in which initialized the new RoleVoter() with default rolePrefix = "ROLE_"; (this makes it impossible to use beans to set your rolePrefix) that give to us not working annotations, because RoleVoter expects annotation value which starts with 'ROLE_'

For resolving this problem i override GlobalMethodSecurityConfiguration like this

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class AppMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
    @Override
    protected AccessDecisionManager accessDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
        ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
        expressionAdvice.setExpressionHandler(getExpressionHandler());
        decisionVoters.add(getRoleVoter());
        decisionVoters.add(new AuthenticatedVoter());
        return new AffirmativeBased(decisionVoters);
    }

    private RoleVoter getRoleVoter() {
        RoleVoter e = new RoleVoter();
        e.setRolePrefix("");
        return e;
    }
}
Share:
40,890
silverhawk
Author by

silverhawk

If you will code bad, Java will come and eat all your memory.

Updated on July 09, 2022

Comments

  • silverhawk
    silverhawk almost 2 years

    I am trying to set up a method security annotation using @Secured("ADMIN") (without any XML, only java config, Spring Boot). But access via roles does not work.

    Security Config:

    @Configuration
    @EnableWebSecurity
    public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter{
    
    .....
    
    @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                    .antMatchers("/api/**").fullyAuthenticated().and()
                    .addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        }
    
    .....
    
    }
    

    I want restrict access to the method of the controller:

    @RestController
    @RequestMapping("/api/groups")
    public class GroupController {
    
        @Autowired
        private GroupService groupService;
    
        @Secured("ADMIN")
        @RequestMapping
        public List<Group> list() {
            return groupService.findAll();
        }
    
    }
    

    Restrict access by the url is working, with:

    .antMatchers("/api/**").hasAuthority("ADMIN")
    

    Maybe I forgot to specify that I want restrict by roles?

    UPD: By the rules, At what layer must be @PreAuthorize("hasRole('ADMIN')") in Controller layer or in Service layer?