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

52次阅读

共计 5479 个字符,预计需要花费 14 分钟才能阅读完成。

写在后面

上回咱们讲了 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 的过滤器链加载流程咱们就说完了。

正文完
 0