Spring Boot Security(2.x.x)鉴权过程概览

上一篇《结合实际场景分析Spring Boot Security(2.x.x)认证过程》我们梳理了整个认证的过程,这一篇我们就来讲一讲鉴权的大致流程。
由于本人并没有自定义鉴权逻辑的需求,所以本文不涉及如何定制化鉴权过程


一、鉴权入口——FilterSecurityInterceptor

《结合实际场景分析Spring Boot Security(2.x.x)认证过程》提到的12个filter中,最后一个是FilterSecurityInterceptor,它就承担了鉴权的重任。

image.png

  1. 进入FilterSecurityInterceptor.class
public void invoke(FilterInvocation fi) throws IOException, ServletException {
        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 && observeOncePerRequest) {
                fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
            }

            // 1. 在真正调用/xxx之前的一些处理,其中包含调用 AccessDecisionManager 来验证当前已认证成功的用户是否有权限访问该资源
            InterceptorStatusToken token = super.beforeInvocation(fi);

            try {
                // 2. 调用/xxx
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            }
            finally {
                super.finallyInvocation(token);
            }

            super.afterInvocation(token, null);
        }
    }
  1. 进入InterceptorStatusToken token = super.beforeInvocation(fi);
...
// Attempt authorization
        try {
            this.accessDecisionManager.decide(authenticated, object, attributes);
        }
...

三个参数分别代表:authenticated——认证的对象,object——url信息,attributes——是否需要认证


image.png
  1. accessDecisionManager提供三种方案,默认选用一票通过制,具体规则可以看代码里的注释


    image.png
/**
     * This concrete implementation simply polls all configured
     * {@link AccessDecisionVoter}s and grants access if any
     * AccessDecisionVoter voted affirmatively. Denies access only if there
     * was a deny vote AND no affirmative votes.
     * 

* If every AccessDecisionVoter abstained from voting, the decision will * be based on the {@link #isAllowIfAllAbstainDecisions()} property (defaults to * false). *

* * @param authentication the caller invoking the method * @param object the secured object * @param configAttributes the configuration attributes associated with the method * being invoked * * @throws AccessDeniedException if access is denied */ public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException { int deny = 0; //默认只有一个voter:WebExpressionVoter.class for (AccessDecisionVoter voter : getDecisionVoters()) { 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; } } if (deny > 0) { throw new AccessDeniedException(messages.getMessage( "AbstractAccessDecisionManager.accessDenied", "Access is denied")); } // To get this far, every AccessDecisionVoter abstained checkAllowIfAllAbstainDecisions(); }
  1. 默认的唯一一个voter:WebExpressionVoter.class投票逻辑:
/**
 * Voter which handles web authorisation decisions.
 * @author Luke Taylor
 * @since 3.0
 */
public class WebExpressionVoter implements AccessDecisionVoter {
    private SecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();

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

        WebExpressionConfigAttribute weca = findConfigAttribute(attributes);

        if (weca == null) {
            return ACCESS_ABSTAIN;
        }

        EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication,
                fi);
        ctx = weca.postProcess(ctx, fi);

        return ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx) ? ACCESS_GRANTED
                : ACCESS_DENIED;
    }

有兴趣的朋友可以研究一下这个voter的角色判断细节。
以上就是鉴权的过程。

二、过程梳理

FilterSecurityInterceptor.class
-> super.beforeInvocation(fi)
-> accessDecisionManager.decide(authenticated, object, attributes)
-> (默认的accessDecisionManager是AffirmativeBased.class 一票通过制)
-> voter.vote(authentication, object, configAttributes)


结语
由于没有定制化开发鉴权的经验,所以本文只是梳理了spring boot security的鉴权过程,让大家对此有个大致的了解,如果需要添加自定义的鉴权过程,还需大家更深入的调研。
感谢这篇《Spring Security源码分析二:Spring Security授权过程》

你可能感兴趣的:(Spring Boot Security(2.x.x)鉴权过程概览)