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();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值