1 系统配置
使用数据库配置系统 资源-角色-用户。
1 资源
资源a: /admin/*
资源b:/admin/admin!index.action
2 角色
超级管理员:role_super
普通管理员:role_normal
3 资源-角色
role_super拥有资源a
role_normal拥有资源b
2 期望效果
超级管理员能够访问所有以/admin/开头的资源(当然包括资源b),普通管理员只能访问首页:/admin/admin!index.action
3 问题描述
有的时候超级管理员不能访问资源b,但是能访问其它的/admin/开头的资源,普通管理员只能访问资源b。
而有的时候,普通管理员无法访问所有/admin/开头的资源(不能访问资源b),超级管理员能访问所有/admin/开头的资源。
4 源码跟踪
角色是否拥有对某个特定资源的访问权限,取决于以下两个投票器:
<bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.vote.RoleVoter" />
<bean class="org.springframework.security.vote.AuthenticatedVoter" />
</list>
</property>
</bean>
角色投票器:
org.springframework.security.vote.RoleVoter
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
int result = ACCESS_ABSTAIN;
Iterator iter = config.getConfigAttributes().iterator();
GrantedAuthority[] authorities = extractAuthorities(authentication);
while (iter.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) iter.next();
if (this.supports(attribute)) {
result = ACCESS_DENIED;
// Attempt to find a matching granted authority
for (int i = 0; i < authorities.length; i++) {
if (attribute.getAttribute().equals(authorities[i].getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
return result;
}
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {
return true;
}
else {
return false;
}
}
简单的观察代码运行此处的三个变量值:
authentication:当前用户的认证信息
org.springframework.security.providers.UsernamePasswordAuthenticationToken@da7bc0ad: Principal: cc.jiuyi.entity.Admin@617667eb; Password: [PROTECTED]; Authenticated: true
; Details: org.springframework.security.ui.WebAuthenticationDetails@380f4: RemoteIpAddress: 127.0.0.1; SessionId: 79F021F693F49C844524B5EECC9B2D08; Granted Authorities: ROLE_NORMAL
object:想要访问的资源
FilterInvocation: URL: /admin/admin!index.action
config:拥有该资源的角色组
[ROLE_ADMIN]
结合代码逻辑,角色投票器的主要作用:
如果没有任何角色拥有该资源,或者所有角色都不是有效角色名,则投弃权票;
否则将用户的角色和角色组一一比对,如果都不匹配,投反对票,若存在匹配,则投赞成票。
5 原因分析
在期望效果中,我希望的逻辑是:
role_super 拥有 admin/*,以及/admin/admin!index.action权限
role_normal 拥有 /admin/admin!index.action权限
但是在spring security的代码中判断逻辑是:
/admin/admin!index.action资源的所属角色有[ROLE_ADMIN]
。
/admin/admin!index.action资源不是明确赋予了[ROLE_NORMAL]
吗?
到这一步,我就没有再细看源码了。猜测的原因是,/admin/admin!index.action匹配了通配符admin/*,spring security就将/admin/admin!index.action这个资源划分到[ROLE_ADMIN]
,并停止了为它寻找其它的所属。
当然,在下一次启动的时候,/admin/admin!index.action资源的所属角色可能变成了[ROLE_NORMAL]
。