关于java:Spring-Security拦截器加载流程分析练气中期

写在后面

上回咱们讲了spring security整合spring springmvc的流程,并且晓得了spring security是通过过滤器链来进行认证受权操作的。明天咱们来剖析一下springsecurity过滤器链的加载流程。读者在浏览本文时能够边浏览边跟着操作,这样子会了解的更分明一些。

Spring Security过滤器链

spring security的过滤器十分多,这里简略介绍几个罕用的过滤器。

spring security常过滤器链介绍

org.springframework.security.web.context.SecurityContextPersistenceFilter

次要是应用SecurityContextRepository在session中保留或更新一个SecurityContext域对象(相当于一个容器),并将SecurityContext给当前的过滤器应用,来为后续filter建设所需的上下文。SecurityContext中存储了以后用户的认证以及权限信息。 其余的过滤器都须要依赖于它。在 Spring Security 中,尽管平安上下文信息被存储于 Session 中,但咱们在理论应用中不应该间接操作 Session,而该当应用 SecurityContextHolder。

org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter

这个过滤器用于集成SecurityContext到Spring异步执行机制的WebAsyncManager 中。如果想要与spring集成,就必须要应用此过滤器链。

org.springframework.security.web.csrf.CsrfFilter

csrf又称跨域申请伪造,SpringSecurity会对所有post申请验证是否蕴含系统生成的csrf的token信息,如果不蕴含,则报错。起到避免csrf攻打的成果。

题外话:csrf攻打

CSRF(Cross-site request forgery)跨站申请伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻打网站发送跨站申请。利用受害者在被攻打网站曾经获取的注册凭证,绕过后盾的用户验证,达到假冒用户对被攻打的网站执行某项操作的目标。

一个例子:

比方你登陆了银行网站A之后,浏览器cookie中将保留你的登录信息。同时你没有没有登记登录的状况下又用同一浏览器拜访了视频网站B,假如视频网站B中含有csrf病毒,其将获取你的cookie内容,拿到你银行网站A的登录信息,就能够进行不可形容之操作了。

而这个CsrfFilter通过签发token的形式进行拜访验证,如果token不是本网站签发的或者拜访申请中不带有这个token则回绝拜访。

org.springframework.security.web.authentication.logout.LogoutFilter

匹配URL为/logout的申请,实现用户退出,革除认证信息。

org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

认证操作全靠这个过滤器,默认匹配URL为/login且必须为POST申请。

org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter

如果没有在配置文件中指定认证页面,则由该过滤器生成一个默认认证页面

org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter

由此过滤器能够生产一个默认的退出登录页面

org.springframework.security.web.authentication.www.BasicAuthenticationFilter

此过滤器会主动解析HTTP申请中头部名字为Authentication,且以Basic结尾的头信息。

org.springframework.security.web.savedrequest.RequestCacheAwareFilter

通过HttpSessionRequestCache外部保护了一个RequestCache,用于缓存HttpServletRequest

org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter

针对ServletRequest进行了一次包装,使得request具备更加丰盛的API

org.springframework.security.web.authentication.AnonymousAuthenticationFilter

当SecurityContextHolder中认证信息为空,则会创立一个匿名用户存入到SecurityContextHolder中。

spring security为了兼容未登录的拜访,也走了一套认证流程,只不过是一个匿名的身份(游客)。

org.springframework.security.web.session.SessionManagementFilter

SecurityContextRepository限度同一用户开启多个会话的数量

org.springframework.security.web.access.ExceptionTranslationFilter

异样转换过滤器位于整个springSecurityFilterChain的前方,用来转换整个链路中呈现的异样

org.springframework.security.web.access.intercept.FilterSecurityInterceptor

获取所配置资源拜访的受权信息,依据SecurityContextHolder中存储的用户信息来决定其是否有权限。

spring security加载流程

在web.xml中,咱们配置了一个名字为“springSecurityFilterChain”的过滤器org.springframework.web.filter.DelegatingFilterProxy。以下是局部源码。

public class DelegatingFilterProxy extends GenericFilterBean {
    @Nullable
    private String contextAttribute;
    @Nullable
    private WebApplicationContext webApplicationContext;
    @Nullable
    private String targetBeanName;
    private boolean targetFilterLifecycle;
    /** 真正加载的过滤器*/
    @Nullable
    private volatile Filter delegate;
    private final Object delegateMonitor;

    protected void initFilterBean() throws ServletException {
        synchronized(this.delegateMonitor) {
            if (this.delegate == null) {
                if (this.targetBeanName == null) {
                    this.targetBeanName = this.getFilterName();
                }

                WebApplicationContext wac = this.findWebApplicationContext();
                if (wac != null) {
                    this.delegate = this.initDelegate(wac);
                }
            }

        }
    }
    /** 过滤器的入口 */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
            synchronized(this.delegateMonitor) {
                delegateToUse = this.delegate;
                if (delegateToUse == null) {
                    WebApplicationContext wac = this.findWebApplicationContext();
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
                    }
                     /** 在做完一系列判断之后,真正要做的就是这一步,初始化delegate*/
                    delegateToUse = this.initDelegate(wac);
                }
                this.delegate = delegateToUse;
            }
        }

        this.invokeDelegate(delegateToUse, request, response, filterChain);
    }

   

    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        String targetBeanName = this.getTargetBeanName();
        Assert.state(targetBeanName != null, "No target bean name set");
        Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);
        if (this.isTargetFilterLifecycle()) {
            delegate.init(this.getFilterConfig());
        }

        return delegate;
    }

    protected void invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        delegate.doFilter(request, response, filterChain);
    }

   
}

接下来咱们在initDelegate办法上打个断点调试一下

最终咱们发现,通过initDelegate办法给delegate初始化,失去一个FilterChainProxy对象。接下来咱们进入FilterChainProxy这个类看看

public class FilterChainProxy extends GenericFilterBean {
。。。。
}

能够发现这也是一个过滤器,那么咱们找dofilter办法。

接下来咱们在doFilterInternal外面打个断点试试,看看能不能失去过滤器链

咱们在这一步失去了一个List<Filter>,点开能够发现正好是前文讲到的一些罕用的过滤器。

咱们再进入到getFilters中看看这些过滤器链在哪

private List<Filter> getFilters(HttpServletRequest request) {
        Iterator var2 = this.filterChains.iterator();

        SecurityFilterChain chain;
        do {
            if (!var2.hasNext()) {
                return null;
            }

            chain = (SecurityFilterChain)var2.next();
        } while(!chain.matches(request));

        return chain.getFilters();
    }

从该办法咱们晓得过滤器链来自于SecurityFilterChain的getFilters办法,接下来,咱们看看这个类

public interface SecurityFilterChain {
    boolean matches(HttpServletRequest var1);

    List<Filter> getFilters();
}

能够发现这是一个接口,在idea中ctrl+H能够发现它只有一个实现类,那就是DefaultSecurityFilterChain

到这一步,咱们能够发现,springSecurity的过滤器链存在于SecurityFilterChain中(springSecurity的过滤器链由SecurityFilterChain负责封装),它只有一个实现类那就是DefaultSecurityFilterChain。也就是说其过滤器链由SecurityFilterChain封装。

至此,springSecurity的过滤器链加载流程咱们就说完了。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理