概述 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在进行投票时只会在自己支持的配置属性上进行投票