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