springsecurity授權源碼梳理
時序圖借用
登陸認證過程中涉及的filter過濾器整理
從圖中可以看出執行的順序。來看看個人認爲比較重要的 幾個Filter 的處理邏輯,
1.UsernamePasswordAuthenticationFilter
上一篇中登陸認證的過程中使用。
2.AnonymousAuthenticationFilter
//org.springframework.security.web.authentication.AnonymousAuthenticationFilter
//創建一個用戶名爲anonymousUser授權爲ROLE_ANONYMOUS
public AnonymousAuthenticationFilter(String key) {
this(key, "anonymousUser", AuthorityUtils.createAuthorityList(new String[]{"ROLE_ANONYMOUS"}));
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//如果前面的過濾器都沒認證通過
if (SecurityContextHolder.getContext().getAuthentication() == null) {
//爲當前的SecurityContextHolder中添加一個匿名的AnonymousAuthenticationToken
SecurityContextHolder.getContext().setAuthentication(this.createAuthentication((HttpServletRequest)req));
}
chain.doFilter(req, res);
}
//創建匿名的AnonymousAuthenticationToken
protected Authentication createAuthentication(HttpServletRequest request) {
AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(this.key, this.principal, this.authorities);
auth.setDetails(this.authenticationDetailsSource.buildDetails(request));
return auth;
}
3.ExceptionTranslationFilter
ExceptionTranslationFilter是異常處理過濾器,處理在系統認證授權過程中拋出的異常,主要處理AuthenticationException和AccessDeniedException異常
//org.springframework.security.web.access.ExceptionTranslationFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
try {
chain.doFilter(request, response);
} catch (IOException var9) {
throw var9;
} catch (Exception var10) {
//判斷是否爲AuthenticationException異常
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var10);
RuntimeException ase = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (ase == null) {
//判斷是否爲AccessDeniedException異常
ase = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}
if (ase == null) {
//如果上面異常沒有匹配到 則判斷是否爲ServletException異常
if (var10 instanceof ServletException) {
throw (ServletException)var10;
}
//如果上面異常沒有匹配到 則判斷是否爲RuntimeException異常
if (var10 instanceof RuntimeException) {
throw (RuntimeException)var10;
}
throw new RuntimeException(var10);
}
if (response.isCommitted()) {
throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", var10);
}
this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
}
}
4.FilterSecurityInterceptor
此過濾器是認證授權過程中最後一個過濾器,該過濾器之後纔是具體的映射請求 如/menu/menus
//org.springframework.security.web.access.intercept.FilterSecurityInterceptor
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
public void init(FilterConfig arg0) throws ServletException {
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
this.invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
if (fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} else {
if (fi.getRequest() != null && this.observeOncePerRequest) {
fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
}
//****重要****
//執行父類org.springframework.security.access.intercept.AbstractSecurityInterceptor的beforeInvocation()方法
//下面講解
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//可以理解爲 **** 纔是具體的映射請求 如/menu/menus ****
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.finallyInvocation(token);
}
super.afterInvocation(token, (Object)null);
}
}
}
5.AbstractSecurityInterceptor
FilterSecurityInterceptor 的父類
//org.springframework.security.access.intercept.AbstractSecurityInterceptor
protected InterceptorStatusToken beforeInvocation(Object object) {
if (!this.getSecureObjectClass().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException();
} else {
//注:獲取自定義匹配規則
//this.obtainSecurityMetadataSource()爲當前類定義的抽象方法
//DefaultFilterInvocationSecurityMetadataSource爲SecurityMetadataSource的子類
//默認調用DefaultFilterInvocationSecurityMetadataSource中的getAttributes()方法
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
if (attributes != null && !attributes.isEmpty()) {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
}
Authentication authenticated = this.authenticateIfRequired();
try {
//*****重點*****
//authenticated 當前認證過的Authentication
//object ??????
//attributes ?????
this.accessDecisionManager.decide(authenticated, object, attributes);
} catch (AccessDeniedException var7) {
this.publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, var7));
throw var7;
}
if (this.publishAuthorizationSuccess) {
this.publishEvent(new AuthorizedEvent(object, attributes, authenticated));
}
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
if (runAs == null) {
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
} else {
SecurityContext origCtx = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
SecurityContextHolder.getContext().setAuthentication(runAs);
return new InterceptorStatusToken(origCtx, true, attributes, object);
}
} else if (this.rejectPublicInvocations) {
throw new IllegalArgumentException();
} else {
this.publishEvent(new PublicInvocationEvent(object));
return null;
}
}
}
//爲獲取attributes使用
public abstract SecurityMetadataSource obtainSecurityMetadataSource();
調試中觀察到
1)object爲當前請求的url 如/menu/menus
2)attributes
默認會調用org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource類中的getAttributes()方法
public Collection<ConfigAttribute> getAttributes(Object object) {
HttpServletRequest request = ((FilterInvocation)object).getRequest();
Iterator var3 = this.requestMap.entrySet().iterator();
Entry entry;
do {
if (!var3.hasNext()) {
return null;
}
entry = (Entry)var3.next();
} while(!((RequestMatcher)entry.getKey()).matches(request));
return (Collection)entry.getValue();
}
6.AccessDecisionManager授權
AccessDecisionManager是接口
Spring Security默認使用AffirmativeBased實現AccessDecisionManager 的 decide 方法來實現授權。
//org.springframework.security.access.vote.AffirmativeBased
public class AffirmativeBased extends AbstractAccessDecisionManager {
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
Iterator var5 = this.getDecisionVoters().iterator();
while(var5.hasNext()) {
//1.調用AccessDecisionVoter的vote()方法進行投票
AccessDecisionVoter voter = (AccessDecisionVoter)var5.next();
int result = voter.vote(authentication, object, configAttributes);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Voter: " + voter + ", returned: " + result);
}
switch(result) {
case -1://投票爲-1 則記錄一下
++deny;
break;
case 1://投票爲1 直接返回
return;
}
}
if (deny > 0) {
//如果有兩個及以上AccessDecisionVoter(姑且稱之爲投票者吧)都投-1,則直接就不通過了
//拋出AccessDeniedException 未授權
throw new AccessDeniedException();
} else {
this.checkAllowIfAllAbstainDecisions();
}
}
}