3

我有一个基于 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的用户尝试访问以下控制器中的/myUrl url 时,我遇到了ACCESS DENIED异常:

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

这是堆栈跟踪(实际上是其中的一部分):

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_SUDOER 可以从更高优先级的角色(此处为 ROLE_ROOT)访问,为什么我的呼叫失败?

为了让它运行,我必须显式添加最高角色 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
        }
    }

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

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

4

0 回答 0