Spring Security @Secured注释和用户权限

时间:2016-04-19 07:33:58

标签: java spring spring-mvc spring-security

这是关于Spring v.4(MVC +安全性)。我已经实现了UserDetailsServiceImpl,其中loadUserByUsername方法内部用户被授予其权限。简单地说:

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

我有安全控制器,其中我用@Secured注释注释了方法:

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

正如您在loadUserByUsername内看到的那样,我明确向用户授予ADMIN角色。 但是当我尝试访问/users时,我得到Access is denied例外:

  

2016-04-19 10:25:16,899 DEBUG(http-nio-8080-exec-9)   [org.springframework.security.web.access.ExceptionTranslationFilter] -   访问被拒绝(用户不是匿名的);委托给   AccessDeniedHandler   org.springframework.security.access.AccessDeniedException:访问是   否认了   org.springframework.security.access.vote.AbstractAccessDecisionManager.checkAllowIfAllAbstainDecisions(AbstractAccessDecisionManager.java:70)     在   org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:88)     在   org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232)     在   org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64)     在   org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)     在   org.springframework.aop.framework.CglibAopProxy $ DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)   ...

(没有@Secured注释一切正常)。

那么,我在这里错过了什么?

2 个答案:

答案 0 :(得分:9)

令人惊讶的是,问题在于角色名称。由于defaultRolePrefix设置为ROLE_(请参阅org.springframework.security.access.vote.RoleVoter类),所有角色的名称都应以ROLE_前缀开头。换句话说,当我改变了

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

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

@Secured("ADMIN")@Secured("ROLE_ADMIN") - 一切都很好。

答案 1 :(得分:3)

我也遇到过这个问题。 Spring Security 4会自动为ROLE_的任何角色添加前缀。有关详细信息,请参阅Spring Security 3.x to 4.x migration guide

禁用ROLE_前缀

根据迁移指南中的建议,以下BeanPostProcessor可用于禁用自动ROLE_前缀:

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

然后将其定义为bean:

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

检查当局

或者,您可以使用@PreAuthorize注释检查权限:

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

确保已启用prePostEnabled

@EnableGlobalMethodSecurity(prePostEnabled = true)