当提供分层角色时,Spring @Preauthorize无法正常工作?

时间:2013-11-05 17:51:13

标签: spring spring-security authorization hierarchy roles

我有一个基于Spring的应用程序,它为用户定义了一组分层的角色,如下所示:

    <bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
        <property name="hierarchy">
            <value>
                ROLE_ROOT > ROLE_SUDOER
                ROLE_SUDOER > ROLE_USER
                ROLE_USER > ROLE_DUMB
            </value>
        </property>
    </bean>

所以在我的AccessDecisionManager中,我用这种方式使用了4个选民:

    <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.web.access.expression.WebExpressionVoter" />
                <bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
                <bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
                    <constructor-arg ref="roleHierarchy" />
                </bean>
                <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
            </list>
        </constructor-arg>
    </bean>

   <sec:global-method-security pre-post-annotations="enabled">
    <sec:expression-handler ref="methodSecurityExpressionHandler"/>
   </sec:global-method-security>

即使我配置了这组分层角色,当具有最高角色 ROLE_ROOT 的用户尝试访问时,我有 ACCESS DENIED 例外以下控制器中的 / myUrl 网址:

    @Controller
    @PreAuthorize("hasRole('ROLE_SUDOER')")
    @RequestMapping(value = "/myUrl")
    public class BuggyController extends AbstractController {

        @RequestMapping(value = "", method = RequestMethod.GET)
        @ResponseBody
        public List<SensitiveItem> getSensitiveItems(final Principal principal)
        {
            // removed for brievety
        }
    }

这是stacktrace(实际上是它的一部分):

10:55:16.012 [ajp-bio-8029-exec-4] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - [ ] - Authorization successful
10:55:16.012 [ajp-bio-8029-exec-4] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - [ ] - RunAsManager did not change Authentication object
10:55:16.013 [ajp-bio-8029-exec-4] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - [ 192.168.xx.yyy] - Secure object: ReflectiveMethodInvocation: public java.util.List com.xxxx.yyyy.BuggyController.getSensitiveItems(java.security.Principal); target is of class [com.xxxx.yyyy.BuggyController.getSensitiveItems]; Attributes: [[authorize: 'hasRole('ROLE_SUDOER')', filter: 'null', filterTarget: 'null']]
10:55:16.013 [ajp-bio-8029-exec-4] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - [ 192.168.xx.yyy] - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@8bc6b2e4: Principal: org.springframework.security.core.userdetails.User@7a85da9c: Username: sudoer; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ROOT; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@2cd90: RemoteIpAddress: 192.168.22.112; SessionId: 8CE7068F50AD373ED2194B76A188BCEF; Granted Authorities: ROLE_ROOT
10:55:16.013 [ajp-bio-8029-exec-4] DEBUG o.s.s.a.h.RoleHierarchyImpl - [ 192.168.xx.yyy] - getReachableGrantedAuthorities() - From the roles [ROLE_ROOT] one can reach [ROLE_ROOT, ROLE_SUDOER, ROLE_USER, ROLE_DUMB] in zero or more steps.
10:55:16.013 [ajp-bio-8029-exec-4] DEBUG o.s.s.access.vote.AffirmativeBased - [ 192.168.xx.yyy] - Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter@65685e97, returned: -1
10:55:16.013 [ajp-bio-8029-exec-4] DEBUG o.s.s.access.vote.AffirmativeBased - [ 192.168.xx.yyy] - Voter: org.springframework.security.access.vote.RoleVoter@29800e27, returned: 0
10:55:16.013 [ajp-bio-8029-exec-4] DEBUG o.s.s.access.vote.AffirmativeBased - [ 192.168.xx.yyy] - Voter: org.springframework.security.access.vote.AuthenticatedVoter@162137ba, returned: 0
10:55:16.015 [ajp-bio-8029-exec-4] WARN  c.d.d.utils.ExceptionResolver - [ 192.168.xx.yyy] - catched an exception: Access is denied
org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]

日志明确指出 ROLE_ROOT 可以向上升级 ROLE_SUDOER

10:55:16.013 [ajp-bio-8029-exec-4] DEBUG o.s.s.a.h.RoleHierarchyImpl - [ 192.168.xx.yyy] - getReachableGrantedAuthorities() - From the roles [ROLE_ROOT] one can reach [ROLE_ROOT, ROLE_SUDOER, ROLE_USER, ROLE_DUMB] in zero or more steps.

所以我的问题是,如果可以从更高优先级的角色(此处为ROLE_ROOT)访问ROLE_SUDOER,为什么我的呼叫失败?

为了让它运行,我必须显式添加最高角色ROLE_ROOT作为方法安全控制表达式的一部分:

    @Controller
    @PreAuthorize("hasAnyRole('ROLE_ROOT', 'ROLE_SUDOER')")
    @RequestMapping(value = "/myUrl")
    public class BuggyController extends AbstractController {

        @RequestMapping(value = "", method = RequestMethod.GET)
        @ResponseBody
        public List<SensitiveItem> getSensitiveItems(final Principal principal)
        {
            // removed for brievety
        }
    }

这真的很愚蠢,如果您的层次结构角色集很大,它会变得相当复杂:而这正是层次结构角色的用途。它允许有效的角色安全控制,以便优先级较高的角色可以包含较低优先级的角色。

有人可以帮我解决这个问题吗?提前谢谢。

0 个答案:

没有答案