8

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?

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • 1
    Possible duplicate of [Spring security added prefix "ROLE\_" to all roles name?](http://stackoverflow.com/questions/33205236/spring-security-added-prefix-role-to-all-roles-name) – ndrone Apr 22 '16 at 15:51
  • Nope, it doesn't. Without knowing this fact it is impossible to find mentioned question. – Andremoniy Apr 22 '16 at 15:53
  • Hi, I found this question by reviewing the close vote queue (from the duplicate vote). It looks like it applies. Do you agree? (I ask that because you didn't accept it as a dupe so I don't want to close it too quickly, maybe I'm missing something). – Tunaki Apr 22 '16 at 16:50
  • 1
    Dear @Tunaki, I guess it was shown to you, because ndrone already coupled it with my one. When I was searching my issue, I didn't found anything similar. Please notice that if potential OP will in same situation as mine, he wouldn't know anything about `ROLE_` prefix. He will search for the question why standard configuration can not work. – Andremoniy Apr 22 '16 at 16:53

2 Answers2

19

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.

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • It happens when you set new role extendng WebSecurityConfigurerAdapter, just like you said. However, if you set the roles in your own implementation of UserDetails (via UserDetailsService), that problem doesnt happen. – Michel Fortes Jan 21 '17 at 02:54
10

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)
cassiomolin
  • 124,154
  • 35
  • 280
  • 359