springsecurity授權源碼解讀

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();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章