spring-security FilterSecurityInterceptor 源码分析

FilterSecurityInterceptor

这个过滤器决定了访问特定路径应该具备的权限,访问的用户的角色,权限是什么?访问的路径需要什么样的角色和权限?这些判断和处理都是由该类进行的

FilterSecurityInterceptor
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    
    

	//默认实现类:ExpressionBasedFilterInvocationSecurityMetadataSource
	private FilterInvocationSecurityMetadataSource securityMetadataSource;

 	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 {
    
    
    	//判断该请求是否已经被 FilterSecurityInterceptor 处理过了
		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) {
    
    
				fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
			}
			//交由 AbstractSecurityInterceptor 校验
			InterceptorStatusToken token = super.beforeInvocation(fi);

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

			super.afterInvocation(token, null);
		}
	}

    public SecurityMetadataSource obtainSecurityMetadataSource() {
    
    
		return this.securityMetadataSource;
	}
}
AbstractSecurityInterceptor
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{
    
    

	private boolean alwaysReauthenticate = false;
    //默认实现类是 AffirmativeBased
    private AccessDecisionManager accessDecisionManager;

	protected InterceptorStatusToken beforeInvocation(Object object) {
    
    
		Assert.notNull(object, "Object was null");
		final boolean debug = logger.isDebugEnabled();

		if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
    
    
			throw new IllegalArgumentException(
					"Security invocation attempted for object "
							+ object.getClass().getName()
							+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
							+ getSecureObjectClass());
		}

		//obtainSecurityMetadataSource()方法由 FilterSecurityInterceptor 实现,返回一个 ExpressionBasedFilterInvocationSecurityMetadataSource 对象。
		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
				.getAttributes(object);

	    //...

        //其实就是调用SecurityContextHolder.getContext().getAuthentication()然后返回该对象。如果 Authentication 还没有认证过,会调用 AuthenticationManager 进行认证。除此之外该类的 alwaysReauthenticate 属性值如果为 true 都会导致重新调用 AuthenticationManager 进行认证
		Authentication authenticated = authenticateIfRequired();

		// Attempt authorization
		try {
    
    
			this.accessDecisionManager.decide(authenticated, object, attributes);
		}
		catch (AccessDeniedException accessDeniedException) {
    
    
			publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
					accessDeniedException));

			throw accessDeniedException;
		}

		if (debug) {
    
    
			logger.debug("Authorization successful");
		}

		//是否发布认证成功的Spring容器事件
		if (publishAuthorizationSuccess) {
    
    
			publishEvent(new AuthorizedEvent(object, attributes, authenticated));
		}

		// Attempt to run as a different user
        //尝试以不同的用户允许
		Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
				attributes);
		//一般情况下都runAs都会为null
		if (runAs == null) {
    
    
			if (debug) {
    
    
				logger.debug("RunAsManager did not change Authentication object");
			}

			// no further work post-invocation
			return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,
					attributes, object);
		}
		else {
    
    
			//...
		}
	}
}
ExpressionBasedFilterInvocationSecurityMetadataSource
public class DefaultFilterInvocationSecurityMetadataSource implements
		FilterInvocationSecurityMetadataSource {
    
    

		//里面保存着用户配置的各种请求路径规则和认证规则
        //requestMap的最后有一个匹配任何请求路径的成员,该成员对象的认证规则为'authenticated'即需要经过认证才可以访问。
        private final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;

        //ConfigAttribute指定该路径是否需要验证,一般需要认证是其内部属性authorizeExpression 为 'authenticated',如果是任何人可以访问,内部属性authorizeExpression 为 'permitAll'
        //获取能匹配到对应接口路径的认证规则
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;
	}
}
AffirmativeBased
public class AffirmativeBased extends AbstractAccessDecisionManager {
    
    

public void decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
    
    
		int deny = 0;
		//getDecisionVoters():默认情况得到是存放着 一个 WebExpressionVoter 对象的列表
		for (AccessDecisionVoter voter : getDecisionVoters()) {
    
    
        	//调用voter进行分析并返回投票结果
			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;
			}
		}
		//只要有一个 AccessDecisionVoter 对象偷拍了拒绝访问,就任务
		if (deny > 0) {
    
    
			throw new AccessDeniedException(messages.getMessage(
					"AbstractAccessDecisionManager.accessDenied", "Access is denied"));
		}

		// To get this far, every AccessDecisionVoter abstained
        //到目前为止,每个AccessDecisionVoter都投了弃权票
        //如果该类的不允许所有投票器弃权 (AbstractAccessDecisionManager#allowIfAllAbstainDecisions;默认为false,就不允许),就会抛出 AccessDeniedException 异常,表示拒绝访问
		checkAllowIfAllAbstainDecisions();
	}

}
WebExpressionVoter
public class WebExpressionVoter implements AccessDecisionVoter<FilterInvocation>{
    
    

  public int vote(Authentication authentication, FilterInvocation fi,
			Collection<ConfigAttribute> attributes) {
    
    
		assert authentication != null;
		assert fi != null;
		assert attributes != null;

		//这里是从attributes中找到属于WebExpressionConfigAttribute类型ConfigAttribute对象,并将其转换成WebExpressionConfigAttribute。
        //每个请求路径都可能会有多个ConfigAttribute对象,但是WebExpressionVoter所用到的只是 attributes 中的第一个属于WebExpressionConfigAttribute类型ConfigAttribute对象
		WebExpressionConfigAttribute weca = findConfigAttribute(attributes);
		//没有找到weca会导致WebExpressionVoter放弃投票,让其他投票器投票。
		if (weca == null) {
    
    
			return ACCESS_ABSTAIN;
		}
		//这里构建spring el表达式的上下文。该上下文的根对象的 SecurityExpressionRoot 对象
		EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication,
				fi);
		ctx = weca.postProcess(ctx, fi);

		//根据表达式调用 SecurityExpressionRoot 对象里的方法。如表达式为 'authenticated' 调用的方法就是 SecurityExpressionRoot # isAuthenticated() 方法;表达式为'permitAll',调用的方法式  SecurityExpressionRoot # permitAll()
        //ACCESS_GRANTED 表示 允许访问
        //ACCESS_DENIED 表示 拒绝访问
		return ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED
				: ACCESS_DENIED;
	}
}

关于spring-securiy注解的校验过程

注解 说明 EL表达式
@Secured 用于判断是否具有角色的。能写在方法或类上
@PreAuthorize 访问方法或类在执行之前先判断权限
@PostAuthorize 方法或类执行结束后判断权限

这个注解并不是交给 FiterSecurityInterceptor 处理,而是交由MethodSecurityInterceptor处理,MethodSecurityInterceptor其实是 Spring AOP 提供的方法拦截器。

FiterSecurityInterceptorMethodSecurityInterceptor都是AbstractSecurityInterceptor的子类,并且校验逻辑都是交给AbstractSecurityInterceptor实现。FiterSecurityInterceptorMethodSecurityInterceptor都是只是控制校验的流程。

FiterSecurityInterceptorMethodSecurityInterceptor的区别只是 前者是java web 过滤器,后者是 AOP的方法拦截器。

MethodSecurityInterceptor
public class MethodSecurityInterceptor extends AbstractSecurityInterceptor implements
		MethodInterceptor {
    
    
	//由AbstractSecurityInterceptor实现认证处理,这里只是控制层
	public Object invoke(MethodInvocation mi) throws Throwable {
    
    
		InterceptorStatusToken token = super.beforeInvocation(mi);

		Object result;
		try {
    
    
			result = mi.proceed();
		}
		finally {
    
    
			super.finallyInvocation(token);
		}
		return super.afterInvocation(token, result);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_30321211/article/details/111675970