上篇回顾

我们已经知道了Spring Security的核心过滤器的创建和原理,本文主要介绍核心过滤器FilterChainProxy是如何在tomcat的ServletContext中生效的。

ServletContext如何拿到FilterChainProxy的过滤器对象

我们都知道,Bean都是存在Spring的Bean工厂里的,
而且在Web项目中ServletFilterListener都要放入ServletContext中。

看下面这张图,ServletContainerInitializer接口提供了一个onStartup()方法,用于在Servlet容器启动时动态注册一些对象到ServletContext中。

官方的解释是:为了支持可以不使用web.xml。提供了ServletContainerInitializer,它可以通过SPI机制,当启动web容器的时候,会自动到添加的相应jar包下找到META-INF/services下以ServletContainerInitializer的全路径名称命名的文件,它的内容为ServletContainerInitializer实现类的全路径,将它们实例化。

通过下图可知,Spring框架通过META-INF配置了SpringServletContainerInitializer

SpringServletContainerInitializer实现了ServletContainerInitializer接口。

请注意该类上的@HandlesTypes(WebApplicationInitializer.class)注解.

根据Sevlet3.0规范,Servlet容器在调用onStartup()方法时,会以Set集合的方式注入WebApplicationInitializer的子类(包括接口,抽象类)。然后会依次调用WebApplicationInitializer的实现类的onStartup方法,从而起到启动web.xml相同的作用(添加servletlistener实例到ServletContext中)。

Spring Security中的AbstractSecurityWebApplicationInitializer就是WebApplicationInitializer的抽象子类.

当执行到下面的onStartup()方法时,会调用insertSpringSecurityFilterChain()

将类型为FilterChainProxy名称为springSecurityFilterChain的过滤器对象用DelegatingFilterProxy包装,然后注入ServletContext

运行过程

请求到达的时候,FilterChainProxy的dofilter()方法内部,会遍历所有的SecurityFilterChain,对匹配到的url,则一一调用SecurityFilterChain中的filter做认证或授权。

public class FilterChainProxy extends GenericFilterBean {private final static String FILTER_APPLIED = FilterChainProxy.class.getName().concat(".APPLIED");private List<SecurityFilterChain> filterChains;private FilterChainValidator filterChainValidator = new NullFilterChainValidator();private HttpFirewall firewall = new StrictHttpFirewall();public FilterChainProxy() {}public FilterChainProxy(SecurityFilterChain chain) {this(Arrays.asList(chain));}public FilterChainProxy(List<SecurityFilterChain> filterChains) {this.filterChains = filterChains;}@Overridepublic void afterPropertiesSet() {filterChainValidator.validate(this);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;if (clearContext) {try {request.setAttribute(FILTER_APPLIED, Boolean.TRUE);doFilterInternal(request, response, chain);}finally {SecurityContextHolder.clearContext();request.removeAttribute(FILTER_APPLIED);}}else {doFilterInternal(request, response, chain);}}private void doFilterInternal(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {FirewalledRequest fwRequest = firewall.getFirewalledRequest((HttpServletRequest) request);HttpServletResponse fwResponse = firewall.getFirewalledResponse((HttpServletResponse) response);// 根据当前请求,获得一组过滤器链List<Filter> filters = getFilters(fwRequest);if (filters == null || filters.size() == 0) {if (logger.isDebugEnabled()) {logger.debug(UrlUtils.buildRequestUrl(fwRequest)+ (filters == null ? " has no matching filters": " has an empty filter list"));}fwRequest.reset();chain.doFilter(fwRequest, fwResponse);return;}VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);// 请求依次经过这组滤器链vfc.doFilter(fwRequest, fwResponse);}/*** 根据Request请求获得一个过滤器链*/private List<Filter> getFilters(HttpServletRequest request) {for (SecurityFilterChain chain : filterChains) {if (chain.matches(request)) {return chain.getFilters();}}return null;}/*** 根据URL获得一个过滤器链*/public List<Filter> getFilters(String url) {return getFilters(firewall.getFirewalledRequest((new FilterInvocation(url, null).getRequest())));}/*** 返回一个过滤器链*/public List<SecurityFilterChain> getFilterChains() {return Collections.unmodifiableList(filterChains);}// 过滤器链内部类private static class VirtualFilterChain implements FilterChain {private final FilterChain originalChain;private final List<Filter> additionalFilters;private final FirewalledRequest firewalledRequest;private final int size;private int currentPosition = 0;private VirtualFilterChain(FirewalledRequest firewalledRequest,FilterChain chain, List<Filter> additionalFilters) {this.originalChain = chain;this.additionalFilters = additionalFilters;this.size = additionalFilters.size();this.firewalledRequest = firewalledRequest;}@Overridepublic void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException {if (currentPosition == size) {if (logger.isDebugEnabled()) {logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)+ " reached end of additional filter chain; proceeding with original chain");}// Deactivate path stripping as we exit the security filter chainthis.firewalledRequest.reset();originalChain.doFilter(request, response);}else {currentPosition++;Filter nextFilter = additionalFilters.get(currentPosition - 1);if (logger.isDebugEnabled()) {logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)+ " at position " + currentPosition + " of " + size+ " in additional filter chain; firing Filter: '"+ nextFilter.getClass().getSimpleName() + "'");}nextFilter.doFilter(request, response, this);}}}public interface FilterChainValidator {void validate(FilterChainProxy filterChainProxy);}private static class NullFilterChainValidator implements FilterChainValidator {@Overridepublic void validate(FilterChainProxy filterChainProxy) {}}}

深入浅出Spring Security(三):FilterChainProxy的运行过程相关推荐

  1. 深入浅出Spring Security(二):FilterChainProxy的创建过程

    上篇回顾 框架的核心是一个过滤器,这个过滤器名字叫springSecurityFilterChain,类型是FilterChainProxy WebSecurity和HttpSecurity都是建造者 ...

  2. Spring Security(三) —— 加密系统

    一:加密系统简介 最早我们使用类似SHA-256这样的单向Hash算法.用户注册成功后,保存在数据库中的不再是用户的明文密码,而是经过SHA-256加密计算的一个字符串,当用户进行登录时,将用户输入的 ...

  3. 深入浅出Spring Security(一):三句话解释框架原理

    三句话解释框架原理 整个框架的核心是一个过滤器,这个过滤器名字叫springSecurityFilterChain类型是FilterChainProxy 核心过滤器里面是过滤器链(列表),过滤器链的每 ...

  4. 运行时动态的开关 Spring Security

    为什么80%的码农都做不了架构师?>>>    1. 为什么要在运行时动态的开关 Spring Security? 考虑这样一个场景,当我们构建了一整套微服务架构的系统后,公司某个内 ...

  5. Spring Security(三十六):12. Spring MVC Test Integration

    Spring Security provides comprehensive integration with Spring MVC Test Spring Security提供与Spring MVC ...

  6. 认证与授权流程与spring boot整合 spring security(1)

    一   spring security 1.1 spring security的作用 Spring Security所解决的问题就是安全访问控制,而安全访问控制功能其实就是对所有进入系统的请求进行拦截 ...

  7. Spring Security 参考手册(一)

    Spring Security 参考手册 Ben AlexLuke TaylorRob WinchGunnar Hillert Spring security 是一个强大的和高度可定制的身份验证和访问 ...

  8. Spring Security中文文档

    Spring Security中文文档 来源:https://www.springcloud.cc/spring-security.html#overall-architecture 作者 Ben A ...

  9. Spring Security 5.0.x 参考手册 【翻译自官方GIT-2018.06.12】

    源码请移步至: https://github.com/aquariuspj/spring-security/tree/translator/docs/manual/src/docs/asciidoc ...

最新文章

  1. iOS动画详解(学习动画看这一篇就够了)
  2. mysql 并行复制搭建_基于GTID的主从实践系列之④并行复制搭建及测试
  3. python读取文件如何去除空格_python读取txt文件时怎么去掉空格
  4. redis创建像mysql表结构_如何给redis添加新数据结构
  5. iOS开发蓝牙 蓝牙4.0的各种踩过的坑,希望你们少踩点
  6. php是独立服务吗,使用Sprockets作为PHP应用程序的独立服务
  7. 小程序 - 参考数据 - ASC字符码表和常用的中文字符编码表
  8. 一个前端妹子的悲欢编程之路
  9. VC MakeUp 操作XML
  10. 家用计算机按键不灵怎么修,空格键失灵了怎么办?电脑键盘按键失灵的解决办法...
  11. android面试简历!Android中高级面试必知必会,内含福利
  12. K3CLOUD成本管理参数解释
  13. 如何解决搜狗浏览器自动填充
  14. 【机器学习】李宏毅-预测PM2.5
  15. 下列为非法的C语言转义字符的项目是,非法的C语言转义字符是()。
  16. Windows 此电脑->设备与驱动器->图标管理
  17. 【动态规划】线性动态规划
  18. 企业抖音账号流量提升3步法,新号也能过百万播放量
  19. 20岁,他来武大读博!
  20. Intellij IDEA如何设置为中文界面?

热门文章

  1. NBT:扩增子及其他测序的最小信息标准和测序规范(MIMARKS)
  2. QIIME 2教程. 16纵向和成对样本比较q2-longitudinal(2020.11)
  3. NBT:宏基因组10X建库+雅典娜算法组装获得微生物高质量基因组
  4. R语言构建xgboost模型:使用xgb.DMatrix保存、加载数据集、使用getinfo函数抽取xgb.DMatrix结构中的数据
  5. R语言e1071包中的支持向量机:仿真数据(螺旋线性不可分数据集)、简单线性核的支持向量机SVM(模型在测试集上的表现、可视化模型预测的结果、添加超平面区域与原始数据标签进行对比分析)、如何改进核函数
  6. pandas使用shift方法进行特征差分
  7. python使用imbalanced-learn的RandomUnderSampler方法进行下采样处理数据不平衡问题
  8. 对偶问题(dual problem)
  9. The impact of third generation genomic technologies on plant genome assembly 第三代基因组技术对植物基因组组装的影响
  10. cli3解决 ie11语法错误 vue_vue-admin-template基于vue2的极简后台管理系统