美文网首页
Spring Boot Security(2.x.x)鉴权过程概

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

作者: Cherron | 来源:发表于2019-08-06 16:03 被阅读0次

上一篇《结合实际场景分析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
     * <code>AccessDecisionVoter</code> voted affirmatively. Denies access only if there
     * was a deny vote AND no affirmative votes.
     * <p>
     * If every <code>AccessDecisionVoter</code> abstained from voting, the decision will
     * be based on the {@link #isAllowIfAllAbstainDecisions()} property (defaults to
     * false).
     * </p>
     *
     * @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<ConfigAttribute> 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<FilterInvocation> {
    private SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler();

    public int vote(Authentication authentication, FilterInvocation fi,
            Collection<ConfigAttribute> 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)鉴权过程概

      本文链接:https://www.haomeiwen.com/subject/gefjlctx.html