概述
授权流程
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:需要所有的投票赞成才能通过
- 1).通过SecurityMetadataSource获取访问资源权限,有两种获取来源
- 3.在默认的AffirmativeBased决策机制中,遍历所有AccessDecisionVoter投票器,根据RoleVoter和AuthenticatedVoter 的投票结果决定是否允许访问
相关链接
首页
上一篇:认证流程篇
下一篇:短信登录篇
最后
以上就是苗条小熊猫为你收集整理的Security之授权流程篇授权流程总结相关链接的全部内容,希望文章能够帮你解决Security之授权流程篇授权流程总结相关链接所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复