Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security @Secured annotation and User authorities

This is about Spring v.4 (MVC + Security). I have implemented UserDetailsServiceImpl, where inside the loadUserByUsername method a user is granted with its authorities. Let's say it's simply:

public UserDetails loadUserByUsername(String username) {
    ...     
    Collection<GrantedAuthority> authorities = new ArrayList<>();
    
    authorities.add(new SimpleGrantedAuthority("ADMIN"));
    
    return new org.springframework.security.core.userdetails.User(username, password, enabled, true, true, true, authorities);
    ...
}

And there is a security controller inside which there is an annotated method with the @Secured annotation:

@Secured("ADMIN")
@RequestMapping(value = "/users", method = RequestMethod.GET)
public String users(Model model ...) { ... }

As you can see inside the loadUserByUsername method I explicitly granted the ADMIN role to the user. But when I'm trying access the /users I get an Access is denied exception:

2016-04-19 10:25:16,899 DEBUG (http-nio-8080-exec-9) [org.springframework.security.web.access.ExceptionTranslationFilter] - Access is denied (user is not anonymous); delegating to AccessDeniedHandler org.springframework.security.access.AccessDeniedException: Access is denied at org.springframework.security.access.vote.AbstractAccessDecisionManager.checkAllowIfAllAbstainDecisions(AbstractAccessDecisionManager.java:70) at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:88) at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232) at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655) ...

(without the @Secured annotation everything works fine).

What have I missed here?

like image 690
Andremoniy Avatar asked Apr 19 '16 07:04

Andremoniy


People also ask

What is the use of @secured annotation?

Using @Secured Annotation. The @Secured annotation is used to specify a list of roles on a method. So, a user only can access that method if she has at least one of the specified roles.

How do I use authorities in Spring Security?

Role as Authority Similarly, in Spring Security, we can think of each Role as a coarse-grained GrantedAuthority that is represented as a String and prefixed with “ROLE“. When using a Role directly, such as through an expression like hasRole(“ADMIN”), we are restricting access in a coarse-grained manner.

What is @PreAuthorize annotation in Spring boot?

Method-level security is implemented by placing the @PreAuthorize annotation on controller methods (actually one of a set of annotations available, but the most commonly used). This annotation contains a Spring Expression Language (SpEL) snippet that is assessed to determine if the request should be authenticated.

What's the difference between @secured and @PreAuthorize in Spring Security?

@Secured and @RolesAllowed are the same the only difference is @RolesAllowed is a standard annotation (i.e. not only spring security) whereas @Secured is spring security only. @PreAuthorize is different in a way that it is more powerful then the other 2. It allows for SpEL expression for a more fine-grained control.


2 Answers

Surprisingly, but the problem was with the roles names. Due to the defaultRolePrefix set to the ROLE_ (see org.springframework.security.access.vote.RoleVoter class) all roles should have names starting with the ROLE_ prefix. In other words, when I've changed

authorities.add(new SimpleGrantedAuthority("ADMIN")); to

authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));

and @Secured("ADMIN") to @Secured("ROLE_ADMIN") - everything became fine.

like image 114
Andremoniy Avatar answered Oct 22 '22 00:10

Andremoniy


I also faced this issue. Spring Security 4 automatically prefixes any role with ROLE_. Refer to the Spring Security 3.x to 4.x migration guide for more details.

Disabling ROLE_ prefixing

As suggested in the migration guide, the following BeanPostProcessor can be used to disable the automatic ROLE_ prefixing:

public class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, 
                                                        PriorityOrdered {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {

        // Remove this if you are not using JSR-250
        if (bean instanceof Jsr250MethodSecurityMetadataSource) {
            ((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(null);
        }

        if (bean instanceof DefaultMethodSecurityExpressionHandler) {
            ((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
        }

        if (bean instanceof DefaultWebSecurityExpressionHandler) {
            ((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
        }

        if (bean instanceof SecurityContextHolderAwareRequestFilter) {
            ((SecurityContextHolderAwareRequestFilter)bean).setRolePrefix("");
        }

        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;
    }

    @Override
    public int getOrder() {
        return PriorityOrdered.HIGHEST_PRECEDENCE;
    }
}

Then define it as a bean:

@Bean
public static DefaultRolesPrefixPostProcessor defaultRolesPrefixPostProcessor() {
    return new DefaultRolesPrefixPostProcessor();
}

Checking authorities

Alternatively you can check the authorities using the @PreAuthorize annotation:

@PreAuthorize("hasAuthority('ADMIN')")

Ensure that prePostEnabled is enabled:

@EnableGlobalMethodSecurity(prePostEnabled = true)
like image 10
cassiomolin Avatar answered Oct 22 '22 01:10

cassiomolin