Security之授权流程篇

授权流程

1.Security 的授权访问是通过 FilterSecurityInterceptor 拦截器实现的

...

public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    ...
	// 拦截器的入口
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        this.invoke(new FilterInvocation(request, response, chain));
    }

   ...

    public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
        if (this.isApplied(filterInvocation) && this.observeOncePerRequest) {
            filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
        } else {
            if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
                filterInvocation.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
            }
			// 调用父类AbstractSecurityInterceptor的beforeInvocation()方法
            InterceptorStatusToken token = super.beforeInvocation(filterInvocation);

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

            super.afterInvocation(token, (Object)null);
        }
    }

    ...
}

2.查看 FilterSecurityInterceptor 的父类 AbstractSecurityInterceptor

...
public abstract class AbstractSecurityInterceptor
		implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {

	...

	protected InterceptorStatusToken beforeInvocation(Object object) {
		...
		
		// 从SecurityMetadataSource获取访问资源所需要的权限信息
		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
		if (CollectionUtils.isEmpty(attributes)) {
			...
		}
		...
		
		// 从上下文获取Authentication认证对象(包含权限信息),如果没有就进行认证
		Authentication authenticated = authenticateIfRequired();
		...
		
		// 尝试授权
		attemptAuthorization(object, attributes, authenticated);
		...

		// 这里是重新创建一个包含访问资源权限跟认证对象拥有权限的认证对象,并存储到上下文
		// 不明白这么做的目的
		Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
		if (runAs != null) {
			...
			return new InterceptorStatusToken(origCtx, true, attributes, object);
		}
		this.logger.trace("Did not switch RunAs authentication since RunAsManager returned null");
		// no further work post-invocation
		return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);

	}

	// 从上下文获取Authentication认证对象(包含权限信息),如果没有就进行认证
	private Authentication authenticateIfRequired() {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication.isAuthenticated() && !this.alwaysReauthenticate) {
			if (this.logger.isTraceEnabled()) {
				this.logger.trace(LogMessage.format("Did not re-authenticate %s before authorizing", authentication));
			}
			return authentication;
		}
		authentication = this.authenticationManager.authenticate(authentication);
		// Don't authenticated.setAuthentication(true) because each provider does that
		if (this.logger.isDebugEnabled()) {
			this.logger.debug(LogMessage.format("Re-authenticated %s before authorizing", authentication));
		}
		SecurityContextHolder.getContext().setAuthentication(authentication);
		return authentication;
	}

	// 尝试授权
	private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,
			Authentication authenticated) {
		try {
			this.accessDecisionManager.decide(authenticated, object, attributes);
		}
		catch (AccessDeniedException ex) {
			if (this.logger.isTraceEnabled()) {
				this.logger.trace(LogMessage.format("Failed to authorize %s with attributes %s using %s", object,
						attributes, this.accessDecisionManager));
			}
			else if (this.logger.isDebugEnabled()) {
				this.logger.debug(LogMessage.format("Failed to authorize %s with attributes %s", object, attributes));
			}
			publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex));
			throw ex;
		}
	}

	...
}

3.获取访问资源所需要的权限
数据源SecurityMetadataSource

... 
public interface SecurityMetadataSource extends AopInfrastructureBean {

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

	Collection<ConfigAttribute> getAllConfigAttributes();

	boolean supports(Class<?> clazz);
}

3.1SecurityMetadataSource有两个实现类,一个DefaultFilterInvocationSecurityMetadataSource
DefaultFilterInvocationSecurityMetadataSource#getAttributes:是从configure中获取访问权限

...
public class DefaultFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    ...
    public Collection<ConfigAttribute> getAttributes(Object object) {
        HttpServletRequest request = ((FilterInvocation)object).getRequest();
        int count = 0;
        Iterator var4 = this.requestMap.entrySet().iterator();

        while(var4.hasNext()) {
            Entry<RequestMatcher, Collection<ConfigAttribute>> entry = (Entry)var4.next();
            if (((RequestMatcher)entry.getKey()).matches(request)) {
                return (Collection)entry.getValue();
            }

            if (this.logger.isTraceEnabled()) {
                Log var10000 = this.logger;
                Object var10002 = entry.getKey();
                Object var10003 = entry.getValue();
                ++count;
                var10000.trace(LogMessage.format("Did not match request to %s - %s (%d/%d)", var10002, var10003, count, this.requestMap.size()));
            }
        }

        return null;
    }

    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

在这里插入图片描述

3.2 另一个实现类 AbstractMethodSecurityMetadataSource
这个是从方法注解上获取访问权限

...
public abstract class AbstractMethodSecurityMetadataSource implements MethodSecurityMetadataSource {

	protected final Log logger = LogFactory.getLog(getClass());

	@Override
	public final Collection<ConfigAttribute> getAttributes(Object object) {
		if (object instanceof MethodInvocation) {
			MethodInvocation mi = (MethodInvocation) object;
			Object target = mi.getThis();
			Class<?> targetClass = null;
			if (target != null) {
				targetClass = (target instanceof Class<?>) ? (Class<?>) target
						: AopProxyUtils.ultimateTargetClass(target);
			}
			Collection<ConfigAttribute> attrs = getAttributes(mi.getMethod(), targetClass);
			if (attrs != null && !attrs.isEmpty()) {
				return attrs;
			}
			if (target != null && !(target instanceof Class<?>)) {
				attrs = getAttributes(mi.getMethod(), target.getClass());
			}
			return attrs;
		}
		throw new IllegalArgumentException("Object must be a non-null MethodInvocation");
	}

	@Override
	public final boolean supports(Class<?> clazz) {
		return (MethodInvocation.class.isAssignableFrom(clazz));
	}

}

在这里插入图片描述

4.上面的两种获取访问权限的方式,是依次进行的,当获取到需要访问权限之后,就开始获取认证对象了
在这里插入图片描述

5.开始尝试授权在这里插入图片描述

6.AccessDecisionManager是访问决策管理器,管理所有投票决策机制
AccessDecisionManager接口有三个实现类,他们通过AccessDecisionVoter投票器完成投票,
三种投票决策机制如下:
AffirmativeBased :只需有一个投票赞成即可通过(默认)
ConsensusBased:需要大多数投票赞成即可通过,平票可以配置
UnanimousBased:需要所有的投票赞成才能通过

...
public interface AccessDecisionManager {

	void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException, InsufficientAuthenticationException;

	boolean supports(ConfigAttribute attribute);

	boolean supports(Class<?> clazz);
}

7 默认的投票 AffirmativeBased
调用接口AccessDecisionVoter#vote,根据返回的结果来决定是否允许访问

...
public class AffirmativeBased extends AbstractAccessDecisionManager {

	public AffirmativeBased(List<AccessDecisionVoter<?>> decisionVoters) {
		super(decisionVoters);
	}
	
	@Override
	@SuppressWarnings({ "rawtypes", "unchecked" })
	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);
			switch (result) {
			case AccessDecisionVoter.ACCESS_GRANTED:
				return;
			case AccessDecisionVoter.ACCESS_DENIED:
				deny++;
				break;
			default:
				break;
			}
		}
		if (deny > 0) {
			throw new AccessDeniedException(
					this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
		}
		// To get this far, every AccessDecisionVoter abstained
		checkAllowIfAllAbstainDecisions();
	}

}

8.AccessDecisionVoter投票器, 实现类主要有 RoleVoter和AuthenticatedVoter。
RoleVoter为最为常见的AccessDecisionVoter,其为简单的权限表示,并以前缀为ROLE_

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

}

...
public class RoleVoter implements AccessDecisionVoter<Object> {

	private String rolePrefix = "ROLE_";

	public String getRolePrefix() {
		return this.rolePrefix;
	}

	public void setRolePrefix(String rolePrefix) {
		this.rolePrefix = rolePrefix;
	}

	@Override
	public boolean supports(ConfigAttribute attribute) {
		return (attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix());
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return true;
	}

	@Override
	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;
				// 匹配成功返回 1
				for (GrantedAuthority authority : authorities) {
					if (attribute.getAttribute().equals(authority.getAuthority())) {
						return ACCESS_GRANTED;
					}
				}
			}
		}
		return result;
	}

	Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {
		return authentication.getAuthorities();
	}
}

...
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 (!this.authenticationTrustResolver.isAnonymous(authentication)
				&& !this.authenticationTrustResolver.isRememberMe(authentication));
	}

	public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
		Assert.notNull(authenticationTrustResolver, "AuthenticationTrustResolver cannot be set to null");
		this.authenticationTrustResolver = authenticationTrustResolver;
	}

	@Override
	public boolean supports(ConfigAttribute attribute) {
		return (attribute.getAttribute() != null) && (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())
				|| IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())
				|| IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute()));
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return true;
	}

	@Override
	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 (this.authenticationTrustResolver.isRememberMe(authentication)
							|| isFullyAuthenticated(authentication)) {
						return ACCESS_GRANTED;
					}
				}
				// 是否匿名访问
				if (IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute())) {
					if (this.authenticationTrustResolver.isAnonymous(authentication)
							|| isFullyAuthenticated(authentication)
							|| this.authenticationTrustResolver.isRememberMe(authentication)) {
						return ACCESS_GRANTED;
					}
				}
			}
		}
		return result;
	}
}

总结

  • 1.FilterSecurityInterceptor#doFilter()方法拦截所有请求,并调用父类
    AbstractSecurityInterceptor#beforeInvocation()方法;
  • 2.在beforeInvocation()方法中,
    • 1).通过SecurityMetadataSource获取访问资源权限,有两种获取来源
      • a.从configure配置中获取DefaultFilterInvocationSecurityMetadataSource
      • b.从方法注解上获取AbstractMethodSecurityMetadataSource
    • 2).从上下文获取认证对象(包含权限信息),上下文没有认证对象则进行认证操作
    • 3).尝试授权,委托给AccessDecisionManager访问决策管理器,提供三种投票决策机制
      • AffirmativeBased :只需有一个投票赞成即可通过(默认)
      • ConsensusBased:需要大多数投票赞成即可通过,平票可以配置
      • UnanimousBased:需要所有的投票赞成才能通过
  • 3.在默认的AffirmativeBased决策机制中,遍历所有AccessDecisionVoter投票器,根据RoleVoter和AuthenticatedVoter 的投票结果决定是否允许访问

相关链接

首页
上一篇:认证流程篇
下一篇:短信登录篇

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值