关于Acegi在对Authentication(用户的权限组)与Resource的ConfigAttributeDefinition(资源权限组)的匹配问题一点解释.
问题提出:
最近在看Acegi配置时出现了一点问题:
在DB中配置了用户A有Role_User,Role_Admin两个权限,配置 一个URL可以使Role_Admin和Role_SuperAdmin访问.
按理来讲,A也有访问权限,但实际中老是出问题,细心看了一下AccessDessionManager的实现类,发现了问题所在:
Spring的处理流程:
1用户请求某一页面
2Spring Acegi Filter Chain根据Filter的拦截后缀拦截这一请求,并送到一系列的Filter中进行过滤.
3首次访问时,当然用户未登陆,此时Authentication是空的,(accessDecisionManager中判断SecurityContextHolder.getContext().getAuthentication()为空,发出一个未找到权限的Event并抛出异常.)
4exceptionTranslationFilter接到这个异常,并把请求转向到EntryPoint配置的登陆入口点(authenticationEntryPoint)
5.用户登陆.此时Filter Chain再次拦截到请求,并转给Filter们.
6.accessDecisionManager获取用户的Authentication组以及资源的ConfigAttributeDefinition权限列表.
7.当accessDecisionManager由UnanimousBased实现时,来看UnanimousBased的代码:
---------------------------------------------------------------
public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
throws AccessDeniedException
{
int grant = 0;
int abstain = 0;
Iterator configIter = config.getConfigAttributes();
while (configIter.hasNext()) {
ConfigAttributeDefinition thisDef = new ConfigAttributeDefinition();
thisDef.addConfigAttribute((ConfigAttribute)configIter.next());
Iterator voters = getDecisionVoters().iterator();
while (voters.hasNext()) {
AccessDecisionVoter voter = (AccessDecisionVoter)voters.next();
int result = voter.vote(authentication, object, thisDef); //要求权限必须N对N的相等
switch (result)
{
case 1:
++grant;
break;
case -1:
throw new AccessDeniedException(this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
default:
++abstain;
}
}
}
if (grant > 0) {
return; //有一张赞成票即可.
}
checkAllowIfAllAbstainDecisions();
}
----------------------------------------------------------------------
可以发现,UnanimousBased要求[每个Voter都不拒绝用户权限与资源权限时才通过.同时必须:资源权限有N个,那么,用户权限也必须包含这N个时才通过.即相当于打开这个资源需要N把钥匙,用户必须同时拥有这N把钥匙才可以.]
接下来看AffirmativeBased 这个决策器的策略:
----------------------------------------------------------------------
Iterator iter = this.getDecisionVoters().iterator();
int deny = 0;
while (iter.hasNext()) {
AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
int result = voter.vote(authentication, object, config); //不要求权限必须相等
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break;
}
}
if (deny > 0) {
throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
"Access is denied"));
} //不能存在否决票
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
------------------------------------------------------------------
可以看出,它只要求用户拥有资源权限N把钥匙中的任何一把,同时所有Voter赞成才可以访问即可以打开这个资源.
还有一个ConsensusBased :
-------------------------------------------------------------------
Iterator iter = this.getDecisionVoters().iterator();
int grant = 0;
int deny = 0;
int abstain = 0;
while (iter.hasNext()) {
AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
int result = voter.vote(authentication, object, config);
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
grant++;
break;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
abstain++;
break;
}
}
if (grant > deny) {
return;
}
if (deny > grant) {
throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
"Access is denied"));
}
if ((grant == deny) && (grant != 0)) {
if (this.allowIfEqualGrantedDeniedDecisions) {
return;
} else {
throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
"Access is denied"));
}
}
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
------------------------------------------------------------
那么这个ConsensusBased 也只需要用户拥有任何一把钥匙,并且Voter们赞成票大于拒绝票时才能访问,当赞成与拒绝票相等时(没有弃权),会判断另一个参数来看配置中是否允许此种情况下访问.
end:可以看出,这三种管理器不仅在Voter的投票策略上是不同的,而且对于用户权限与资源权限配置存在多对多情况下的认证策略也有不同,UanimouseBased最为严格.