深入浅出Spring Security(三):FilterChainProxy的运行过程
上篇回顾
我们已经知道了Spring Security
的核心过滤器的创建和原理,本文主要介绍核心过滤器FilterChainProxy
是如何在tomcat的ServletContext
中生效的。
ServletContext
如何拿到FilterChainProxy
的过滤器对象
我们都知道,Bean都是存在Spring的Bean工厂里的,
而且在Web项目中Servlet
、Filter
、Listener
都要放入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相同的作用(添加servlet
,listener
实例到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的运行过程相关推荐
- 深入浅出Spring Security(二):FilterChainProxy的创建过程
上篇回顾 框架的核心是一个过滤器,这个过滤器名字叫springSecurityFilterChain,类型是FilterChainProxy WebSecurity和HttpSecurity都是建造者 ...
- Spring Security(三) —— 加密系统
一:加密系统简介 最早我们使用类似SHA-256这样的单向Hash算法.用户注册成功后,保存在数据库中的不再是用户的明文密码,而是经过SHA-256加密计算的一个字符串,当用户进行登录时,将用户输入的 ...
- 深入浅出Spring Security(一):三句话解释框架原理
三句话解释框架原理 整个框架的核心是一个过滤器,这个过滤器名字叫springSecurityFilterChain类型是FilterChainProxy 核心过滤器里面是过滤器链(列表),过滤器链的每 ...
- 运行时动态的开关 Spring Security
为什么80%的码农都做不了架构师?>>> 1. 为什么要在运行时动态的开关 Spring Security? 考虑这样一个场景,当我们构建了一整套微服务架构的系统后,公司某个内 ...
- Spring Security(三十六):12. Spring MVC Test Integration
Spring Security provides comprehensive integration with Spring MVC Test Spring Security提供与Spring MVC ...
- 认证与授权流程与spring boot整合 spring security(1)
一 spring security 1.1 spring security的作用 Spring Security所解决的问题就是安全访问控制,而安全访问控制功能其实就是对所有进入系统的请求进行拦截 ...
- Spring Security 参考手册(一)
Spring Security 参考手册 Ben AlexLuke TaylorRob WinchGunnar Hillert Spring security 是一个强大的和高度可定制的身份验证和访问 ...
- Spring Security中文文档
Spring Security中文文档 来源:https://www.springcloud.cc/spring-security.html#overall-architecture 作者 Ben A ...
- Spring Security 5.0.x 参考手册 【翻译自官方GIT-2018.06.12】
源码请移步至: https://github.com/aquariuspj/spring-security/tree/translator/docs/manual/src/docs/asciidoc ...
最新文章
- iOS动画详解(学习动画看这一篇就够了)
- mysql 并行复制搭建_基于GTID的主从实践系列之④并行复制搭建及测试
- python读取文件如何去除空格_python读取txt文件时怎么去掉空格
- redis创建像mysql表结构_如何给redis添加新数据结构
- iOS开发蓝牙 蓝牙4.0的各种踩过的坑,希望你们少踩点
- php是独立服务吗,使用Sprockets作为PHP应用程序的独立服务
- 小程序 - 参考数据 - ASC字符码表和常用的中文字符编码表
- 一个前端妹子的悲欢编程之路
- VC MakeUp 操作XML
- 家用计算机按键不灵怎么修,空格键失灵了怎么办?电脑键盘按键失灵的解决办法...
- android面试简历!Android中高级面试必知必会,内含福利
- K3CLOUD成本管理参数解释
- 如何解决搜狗浏览器自动填充
- 【机器学习】李宏毅-预测PM2.5
- 下列为非法的C语言转义字符的项目是,非法的C语言转义字符是()。
- Windows 此电脑->设备与驱动器->图标管理
- 【动态规划】线性动态规划
- 企业抖音账号流量提升3步法,新号也能过百万播放量
- 20岁,他来武大读博!
- Intellij IDEA如何设置为中文界面?
热门文章
- NBT:扩增子及其他测序的最小信息标准和测序规范(MIMARKS)
- QIIME 2教程. 16纵向和成对样本比较q2-longitudinal(2020.11)
- NBT:宏基因组10X建库+雅典娜算法组装获得微生物高质量基因组
- R语言构建xgboost模型:使用xgb.DMatrix保存、加载数据集、使用getinfo函数抽取xgb.DMatrix结构中的数据
- R语言e1071包中的支持向量机:仿真数据(螺旋线性不可分数据集)、简单线性核的支持向量机SVM(模型在测试集上的表现、可视化模型预测的结果、添加超平面区域与原始数据标签进行对比分析)、如何改进核函数
- pandas使用shift方法进行特征差分
- python使用imbalanced-learn的RandomUnderSampler方法进行下采样处理数据不平衡问题
- 对偶问题(dual problem)
- The impact of third generation genomic technologies on plant genome assembly 第三代基因组技术对植物基因组组装的影响
- cli3解决 ie11语法错误 vue_vue-admin-template基于vue2的极简后台管理系统