SpringSecurity(三)授权流程

授权流程

授权流程图

1、拦截请求,已认证用户访问受保护的web资源将被SecurityFilterChain中(实现类为DefaultSecurityFilterChain)的 FilterSecurityInterceptor 的子类拦截。

FilterSecurityInterceptor:

	
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		FilterInvocation fi = new FilterInvocation(request, response, chain);
		invoke(fi);
	}

	......

	public void invoke(FilterInvocation fi) throws IOException, ServletException {
		if ((fi.getRequest() != null)
				&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
				&& observeOncePerRequest) {
			// filter already applied to this request and user wants us to observe
			// once-per-request handling, so don't re-do security checking
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		}
		else {
			// first time this request being called, so perform security checking
			if (fi.getRequest() != null && observeOncePerRequest) {
				fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
			}

			InterceptorStatusToken token = super.beforeInvocation(fi);

			try {
				fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
			}
			finally {
				super.finallyInvocation(token);
			}

			super.afterInvocation(token, null);
		}
	}

其中,InterceptorStatusToken token = super.beforeInvocation(fi);将会调用父类AbstractSecurityInterceptor的beforeInvocation方法:

	protected InterceptorStatusToken beforeInvocation(Object object) {
		......

		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
				.getAttributes(object);

		......

		// Attempt authorization
		try {
			this.accessDecisionManager.decide(authenticated, object, attributes);
		}

		......
	}

2.获取资源访问策略,AbstractSecurityInterceptor的beforeInvocation方法会从 SecurityMetadataSource 的子类DefaultFilterInvocationSecurityMetadataSource 获取要访问当前资源所需要的权限Collection 。SecurityMetadataSource其实就是读取访问策略的抽象,而读取的内容就是我们配置的访问规则。

接口SecurityMetadataSource的getAttributes方法:

Collection<ConfigAttribute> getAttributes(Object object)
      throws IllegalArgumentException;

子类DefaultFilterInvocationSecurityMetadataSource的getAttributes方法:

public Collection<ConfigAttribute> getAttributes(Object object) {
   final HttpServletRequest request = ((FilterInvocation) object).getRequest();
   for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
         .entrySet()) {
      if (entry.getKey().matches(request)) {
         return entry.getValue();
      }
   }
   return null;
}

3.授权决策,AbstractSecurityInterceptor的beforeInvocation方法会调用 AccessDecisionManager 的子类(AffirmativeBased,ConsensusBasesd和UnanimaousBased)的decide方法,进行授权决策。

接口AccessDecisionManager的decide方法:

/*
  通过传递的参数来决定用户是否有访问对应受保护资源的权限
  authentication:要访问资源的访问者的身份
  object:要访问的受保护资源,web请求对应FilterInvocation
  configAttributes:是受保护资源的访问策略,通过SecurityMetadataSource获取。
*/
void decide(Authentication authentication, Object object,
      Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
      InsufficientAuthenticationException;

决策流程

AccessDecisionManager中包含了一系列的AccessDecisionVoter讲会被用来对Authentication是否有权访问受保护对象进行投票,AccessDecisionManager根据投票结果,做出最终决策。

AbstractAccessDecisionManager中的AccessDecisionVoter:

......
private List<AccessDecisionVoter<? extends Object>> decisionVoters;
......
public List<AccessDecisionVoter<? extends Object>> getDecisionVoters() {
  return this.decisionVoters;
}  

接口AccessDecisionVoter:

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);
}

AccessDecisionManager 的子类–AffirmativeBased,ConsensusBasesd和UnanimaousBased,会根据投票结果进行决策:

AffirmativeBased是Spring Security默认使用的投票方式:只要有一个投票通过,就表示通过。

​ 1、只要有一个投票通过了,就表示通过。

​ 2、如果全部弃权也表示通过。

​ 3、如果没有人投赞成票,但是有人投反对票,则抛出AccessDeniedException.

decide方法:

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"));
   }

   // To get this far, every AccessDecisionVoter abstained
   checkAllowIfAllAbstainDecisions();
}

ConsensusBased:多数赞成就通过。

​ 1、如果赞成票多于反对票则表示通过

​ 2、如果反对票多于赞成票则抛出AccessDeniedException

​ 3、如果赞成票与反对票相同且不等于0,并且属性allowIfEqualGrantedDeniedDecisions的值为true,则表示通过,否则抛出AccessDeniedException。参数allowIfEqualGrantedDeniedDecisions的值默认是true。

​ 4、如果所有的AccessDecisionVoter都弃权了,则将视参数allowIfAllAbstainDecisions的值而定,如果该值为true则表示通过,否则将抛出异常AccessDeniedException。参数allowIfAllAbstainDecisions的值默认为false。

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"));      }   }   // To get this far, every AccessDecisionVoter abstained   checkAllowIfAllAbstainDecisions();}

UnanimousBased:一票否决。

​ 1、如果受保护对象配置的某一个ConfifigAttribute被任意的AccessDecisionVoter反对了,则将抛出AccessDeniedException。

​ 2、如果没有反对票,但是有赞成票,则表示通过。

​ 3、如果全部弃权了,则将视参数allowIfAllAbstainDecisions的值而定,true则通过,false则抛出AccessDeniedException。

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;         }      }   }   // To get this far, there were no deny votes   if (grant > 0) {      return;   }   // To get this far, every AccessDecisionVoter abstained   checkAllowIfAllAbstainDecisions();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值