我有一个基于 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
}
}
这真的很愚蠢,因为如果您的分层角色集很大,它会变得非常复杂:这正是分层角色的用途。它允许有效的角色安全控制,以便较高优先级的角色可以包含较低优先级的角色。
有人可以帮我解决这个问题吗?提前致谢。