概述 AccessDecisionManager访问决策管理器,制定最终访问控制(授权)决策。它的实现类有以下三种
它们都继承了AbstractAccessDecisionManager,先来看一下它的源码
AbstractAccessDecisionManager 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 public abstract class AbstractAccessDecisionManager implements AccessDecisionManager , InitializingBean, MessageSourceAware { protected final Log logger = LogFactory.getLog(getClass()); private List<AccessDecisionVoter<? extends Object >> decisionVoters; protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); private boolean allowIfAllAbstainDecisions = false ; protected AbstractAccessDecisionManager ( List<AccessDecisionVoter<? extends Object>> decisionVoters) { Assert.notEmpty(decisionVoters, "A list of AccessDecisionVoters is required" ); this .decisionVoters = decisionVoters; } public void afterPropertiesSet () throws Exception { Assert.notEmpty(this .decisionVoters, "A list of AccessDecisionVoters is required" ); Assert.notNull(this .messages, "A message source must be set" ); } protected final void checkAllowIfAllAbstainDecisions () { if (!this .isAllowIfAllAbstainDecisions()) { throw new AccessDeniedException (messages.getMessage( "AbstractAccessDecisionManager.accessDenied" , "Access is denied" )); } } public List<AccessDecisionVoter<? extends Object >> getDecisionVoters() { return this .decisionVoters; } public boolean isAllowIfAllAbstainDecisions () { return allowIfAllAbstainDecisions; } public void setAllowIfAllAbstainDecisions (boolean allowIfAllAbstainDecisions) { this .allowIfAllAbstainDecisions = allowIfAllAbstainDecisions; } public void setMessageSource (MessageSource messageSource) { this .messages = new MessageSourceAccessor (messageSource); } public boolean supports (ConfigAttribute attribute) { for (AccessDecisionVoter voter : this .decisionVoters) { if (voter.supports(attribute)) { return true ; } } return false ; } public boolean supports (Class<?> clazz) { for (AccessDecisionVoter voter : this .decisionVoters) { if (!voter.supports(clazz)) { return false ; } } return true ; } }
源码比较简单,主要是维护了一个AccessDecisionVoter列表,AccessDecisionVoter的作用是为AccessDecisionManager做出决定时提供依据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class AffirmativeBased extends AbstractAccessDecisionManager { public AffirmativeBased (List<AccessDecisionVoter<? extends Object>> decisionVoters) { super (decisionVoters); } public void decide (Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException { int deny = 0 ; for (AccessDecisionVoter voter : getDecisionVoters()) { int result = voter.vote(authentication, object, configAttributes); if (logger.isDebugEnabled()) { logger.debug("Voter: " + voter + ", returned: " + result); } 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" )); } checkAllowIfAllAbstainDecisions(); } }
只要有一个投票者投了授权访问,则授予访问权限
只要有一个投票者投了拒绝访问,则拒绝访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public class ConsensusBased extends AbstractAccessDecisionManager { private boolean allowIfEqualGrantedDeniedDecisions = true ; public ConsensusBased (List<AccessDecisionVoter<? extends Object>> decisionVoters) { super (decisionVoters); } public void decide (Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException { int grant = 0 ; int deny = 0 ; for (AccessDecisionVoter voter : getDecisionVoters()) { int result = voter.vote(authentication, object, configAttributes); if (logger.isDebugEnabled()) { logger.debug("Voter: " + voter + ", returned: " + result); } switch (result) { case AccessDecisionVoter.ACCESS_GRANTED: grant++; break ; case AccessDecisionVoter.ACCESS_DENIED: deny++; break ; default : 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" )); } } checkAllowIfAllAbstainDecisions(); } public boolean isAllowIfEqualGrantedDeniedDecisions () { return allowIfEqualGrantedDeniedDecisions; } public void setAllowIfEqualGrantedDeniedDecisions ( boolean allowIfEqualGrantedDeniedDecisions) { this .allowIfEqualGrantedDeniedDecisions = allowIfEqualGrantedDeniedDecisions; } }
授权访问票数多于拒绝访问票数则允许访问
拒绝访问票数多于授权访问票数则拒绝访问
授权访问票数和拒绝访问票数一致的情况并且不为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class UnanimousBased extends AbstractAccessDecisionManager { public UnanimousBased (List<AccessDecisionVoter<? extends Object>> decisionVoters) { super (decisionVoters); } public void decide (Authentication authentication, Object object, Collection<ConfigAttribute> attributes) throws AccessDeniedException { int grant = 0 ; List<ConfigAttribute> singleAttributeList = new ArrayList <>(1 ); singleAttributeList.add(null ); for (ConfigAttribute attribute : attributes) { singleAttributeList.set(0 , attribute); for (AccessDecisionVoter voter : getDecisionVoters()) { int result = voter.vote(authentication, object, singleAttributeList); if (logger.isDebugEnabled()) { logger.debug("Voter: " + voter + ", returned: " + result); } switch (result) { case AccessDecisionVoter.ACCESS_GRANTED: grant++; break ; case AccessDecisionVoter.ACCESS_DENIED: throw new AccessDeniedException (messages.getMessage( "AbstractAccessDecisionManager.accessDenied" , "Access is denied" )); default : break ; } } } if (grant > 0 ) { return ; } checkAllowIfAllAbstainDecisions(); } }
UnanimousBased和其它两种决定策略有一点不同的地方是,其它两种决定策略都是让投票者一次性对所有的已配置的属性进行投票,而UnanimousBased是让每一个投票者单独给每一个配置的属性进行投票。打个比方,例如有一个接口配置了需要拥有admin或者dba两种角色其中一个才能访问,此时有一个用户的角色是admin,如果按照AffirmativeBased的决定策略,因为用户有admin角色,所以最终投票为授权访问,但是换成UnanimousBased的话,它会把admin和dba分别传递给投票者去投票,由于只有admin角色,所以在对dba角色进行投票时会抛出AccessDeniedException,最终导致投票为拒绝访问。
把配置好的属性逐个让投票者进行投票
只要有一个投票者对其中的一个配置属性投了反对票则拒绝访问
所有的投票者对所有的配置属性投了赞成票则允许访问
AccessDecisionVoter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public interface AccessDecisionVoter <S> { int ACCESS_GRANTED = 1 ; int ACCESS_ABSTAIN = 0 ; int ACCESS_DENIED = -1 ; boolean supports (ConfigAttribute attribute) ; boolean supports (Class<?> clazz) ; int vote (Authentication authentication, S object, Collection<ConfigAttribute> attributes) ;}
由上面的访问决策者我们可以得知,它们做出决定其实是通过投票者的投票来做出相应的决策的,在投票者内部维护的三个常量1,0,-1分别代表授权访问,弃权访问,拒绝访问,在它们投票之后会返回这些值,最终AccessDecisionManager根据这些投票者的返回这做出相应的访问策略
常用的投票者主要有以上几种,接下来我们逐一分析它们的工作原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 public class AuthenticatedVoter implements AccessDecisionVoter <Object> { public static final String IS_AUTHENTICATED_FULLY = "IS_AUTHENTICATED_FULLY" ; public static final String IS_AUTHENTICATED_REMEMBERED = "IS_AUTHENTICATED_REMEMBERED" ; public static final String IS_AUTHENTICATED_ANONYMOUSLY = "IS_AUTHENTICATED_ANONYMOUSLY" ; private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl (); private boolean isFullyAuthenticated (Authentication authentication) { return (!authenticationTrustResolver.isAnonymous(authentication) && !authenticationTrustResolver .isRememberMe(authentication)); } public void setAuthenticationTrustResolver ( AuthenticationTrustResolver authenticationTrustResolver) { Assert.notNull(authenticationTrustResolver, "AuthenticationTrustResolver cannot be set to null" ); this .authenticationTrustResolver = authenticationTrustResolver; } public boolean supports (ConfigAttribute attribute) { if ((attribute.getAttribute() != null ) && (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute()) || IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute()) || IS_AUTHENTICATED_ANONYMOUSLY .equals(attribute.getAttribute()))) { return true ; } else { return false ; } } public boolean supports (Class<?> clazz) { return true ; } public int vote (Authentication authentication, Object object, Collection<ConfigAttribute> attributes) { int result = ACCESS_ABSTAIN; for (ConfigAttribute attribute : attributes) { if (this .supports(attribute)) { result = ACCESS_DENIED; if (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())) { if (isFullyAuthenticated(authentication)) { return ACCESS_GRANTED; } } if (IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())) { if (authenticationTrustResolver.isRememberMe(authentication) || isFullyAuthenticated(authentication)) { return ACCESS_GRANTED; } } if (IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute())) { if (authenticationTrustResolver.isAnonymous(authentication) || isFullyAuthenticated(authentication) || authenticationTrustResolver.isRememberMe(authentication)) { return ACCESS_GRANTED; } } } } return result; } }
配置属性是IS_AUTHENTICATED_FULLY,IS_AUTHENTICATED_REMEMBERED,IS_AUTHENTICATED_ANONYMOUSLY三者之一时才进行投票,否则投弃权票
配置的属性是已认证,只有当前认证信息不是匿名用户和记住我用户才投授权访问
配置的属性是记住我,只有当前认证信息是记住我或者是全部认证才投授权访问
配置的属性是匿名,只有当前认证信息是匿名或者是全部认证或者是记住我才投授权访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class PreInvocationAuthorizationAdviceVoter implements AccessDecisionVoter <MethodInvocation> { protected final Log logger = LogFactory.getLog(getClass()); private final PreInvocationAuthorizationAdvice preAdvice; public PreInvocationAuthorizationAdviceVoter (PreInvocationAuthorizationAdvice pre) { this .preAdvice = pre; } public boolean supports (ConfigAttribute attribute) { return attribute instanceof PreInvocationAttribute; } public boolean supports (Class<?> clazz) { return MethodInvocation.class.isAssignableFrom(clazz); } public int vote (Authentication authentication, MethodInvocation method, Collection<ConfigAttribute> attributes) { PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes); if (preAttr == null ) { return ACCESS_ABSTAIN; } boolean allowed = preAdvice.before(authentication, method, preAttr); return allowed ? ACCESS_GRANTED : ACCESS_DENIED; } private PreInvocationAttribute findPreInvocationAttribute ( Collection<ConfigAttribute> config) { for (ConfigAttribute attribute : config) { if (attribute instanceof PreInvocationAttribute) { return (PreInvocationAttribute) attribute; } } return null ; } }
配置属性是PreInvocationAttribute类型的才进行投票,否则投弃权票
根据aop前置通知返回的结果进行投票
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public class RoleVoter implements AccessDecisionVoter <Object> { private String rolePrefix = "ROLE_" ; public String getRolePrefix () { return rolePrefix; } public void setRolePrefix (String rolePrefix) { this .rolePrefix = rolePrefix; } public boolean supports (ConfigAttribute attribute) { if ((attribute.getAttribute() != null ) && attribute.getAttribute().startsWith(getRolePrefix())) { return true ; } else { return false ; } } public boolean supports (Class<?> clazz) { return true ; } public int vote (Authentication authentication, Object object, Collection<ConfigAttribute> attributes) { if (authentication == null ) { return ACCESS_DENIED; } int result = ACCESS_ABSTAIN; Collection<? extends GrantedAuthority > authorities = extractAuthorities(authentication); for (ConfigAttribute attribute : attributes) { if (this .supports(attribute)) { result = ACCESS_DENIED; for (GrantedAuthority authority : authorities) { if (attribute.getAttribute().equals(authority.getAuthority())) { return ACCESS_GRANTED; } } } } return result; } Collection<? extends GrantedAuthority > extractAuthorities( Authentication authentication) { return authentication.getAuthorities(); } }
配置属性是以ROLE_开头才进行投票,否则投弃权票
只要认证信息中有一个角色和已配置的属性匹配则投授权访问,否则投拒绝访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class WebExpressionVoter implements AccessDecisionVoter <FilterInvocation> { private SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler (); public int vote (Authentication authentication, FilterInvocation fi, Collection<ConfigAttribute> attributes) { assert authentication != null ; assert fi != null ; assert attributes != null ; WebExpressionConfigAttribute weca = findConfigAttribute(attributes); if (weca == null ) { return ACCESS_ABSTAIN; } EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication, fi); ctx = weca.postProcess(ctx, fi); return ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED : ACCESS_DENIED; } private WebExpressionConfigAttribute findConfigAttribute ( Collection<ConfigAttribute> attributes) { for (ConfigAttribute attribute : attributes) { if (attribute instanceof WebExpressionConfigAttribute) { return (WebExpressionConfigAttribute) attribute; } } return null ; } public boolean supports (ConfigAttribute attribute) { return attribute instanceof WebExpressionConfigAttribute; } public boolean supports (Class<?> clazz) { return FilterInvocation.class.isAssignableFrom(clazz); } public void setExpressionHandler ( SecurityExpressionHandler<FilterInvocation> expressionHandler) { this .expressionHandler = expressionHandler; } }
配置属性是WebExpressionConfigAttribute类型的才进行投票,否则投弃权票
根据表达式执行结果投票
总结
AccessDecisionManager做出决策时是根据AccessDecisionVoter投票的结果做出决定的
一共有三种AccessDecisionManager,分别是AffirmativeBased,ConsensusBased和UnanimousBased。它们都会根据投票者的投票数作出自己的决定
AccessDecisionVoter在进行投票时只会在自己支持的配置属性上进行投票