当前位置: 首页 > news >正文

Spring Security与Spring Boot集成原理

Spring Security依赖的是过滤器机制,首先是web容器例如tomcat作为独立的产品,本身有自己的一套过滤器机制用来处理请求,那么如何将tomcat接收到的请求转入到Spring Security的处理逻辑呢?spring充分采用了tomcat的拓展机制提供了tomcat过滤器的一个拓展,也就是中间加了一个适配器。这样当请求过来之后,请求经过web容器的过滤器的时候就会进入spring的过滤器流程,从而进入security的过滤器流程。

Tomcat的拓展机制

根据servlet规范:

从Servlet3.0开始,在ServletContext中添加了方法支持在使用编程的方式来自定义servlet,filter和url pattern。有两种方式来定义这些:

  1. contexInitialized method of a ServletContextListener implementation;
  2. the onStartup method of a ServletContainerInitializer implementation;

我们来看通过外部ServletContainerInitializer实现的加载方式,官方定义:在服务启动的时候会通过java的spi机制去查找ServletContainerInitializer类,并且每个应用在启动的时候会实例化ServletContainerInitializer。我们需要将实现放到META-INF/services/javax.servlet.ServletContainerInitializer文件中。

For each application, an instance of the ServletContainerInitializer is created by the container at application startup time.

ServletContainerInitializer会在服务启动的时候并且所有的servlet监听器调用之前被调用。

在文档中还描述了一个重要的注解就是:HandlesType,这个注解的作用是:在ServletContainerInitializer 的 onStartup 方法会被调用的时候,会传入一个 Set 集合,该集合包含的类要么是扩展/实现了初始化器所感兴趣(通过 @HandlesTypes 注解指定)的类,要么是带有通过 @HandlesTypes 注解指定的任何类作为注解的类。

The onStartup method of the ServletContainerInitializer is called with a Set of Classes that either extend / implement the classes that the initializer expressed interest in or if it is annotated with any of the classes specified via the @HandlesTypes annotation.

在了解了上面的前提之后,我们来看Spring进行的拓展,查看spring-web中文件META-INF/services/javax.servlet.ServletContainerInitializer会发现里面有一个实现类:org.springframework.web.SpringServletContainerInitializer,而这也正是一切的开始。

SpringServletContainerInitializer

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {@Overridepublic void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)throws ServletException {......}
}

我们首先注意到在上面的实现中,@HandlesTypes注解指定的是WebApplicationInitializer类,因此在onStartup方法的参数webAppInitializerClasses中我们就能获取到所有该接口的实现类。之后便是通过反射进行实例化和执行所有WebApplicationInitializer实现类的onStartup方法。

查看WebApplicationInitializer接口的实现类可以看到:

由于引了包的缘故,这里看到了web、webmvc和security下面的实现。这里我们看到了熟悉的ContextLoader,当还在使用web.xml集成配置spring的时候,我们配置过<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>,在这里使用AbstractContextLoaderInitializer以编程的方式实现了同样的效果。同样的道理还有AbstractDispatcherServletInitializer,也使用编程的方式替换掉了我们的web.xml配置。

<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

那Spring Security的实现呢,我们还看到了AbstractSecurityWebApplicationInitializer这个实现类?

AbstractSecurityWebApplicationInitializer

tomcat容器在执行的时候会执行过滤器链FilterChain。同时为拓展filter提供了标准,我们可以通过实现Filter接口来注册我们自己的Filter到FilterChain中,但是tomcat容器不会感知到spring容器中的Filter实例。
因此spring提供了一个Filter的实现:DelegatingFilterProxy ,从而将spring中的过滤器的逻辑加入到tomcat容器中。

我们查看Spring Security的官网,对这个架构图不会陌生:

为了和spring的集成,Spring Security利用spring提供的过滤器的拓展点,提供了一个Spring Security的统一的入口:FilterChainProxy,在集成之后执行过滤器的时候会通过:tomcat Filter->spring filter->spring security filter调用链路进行调用。

onStartup

我们来看一下AbstractSecurityWebApplicationInitializeronStartup方法的实现逻辑:

insertSpringSecurityFilterChain方法的实现:

在这里创建了一个DelegatingFilterProxy对象,并且传递的filterName是DEFAULT_FILTER_NAME,这个常量是“springSecurityFilterChain”。这里需要注意的是这个传递的是filterName是一个bean名称。
在第一次接收到请求的时候,在doFilter方法中会使用单例模式从spring容器中获取bean名称的实例,生成一个唯一的Filter对象。通过调试最后得到的是:FilterChainProxy对象。

从这里我们可以知道,如果我们拓展一套自己的过滤器逻辑,则可以把DelegatingFilterProxy当作入口,filterName参数使用我们自己的filter bean名称就可以了。

总结

在servlet3.0之后,我们可以将之前web.xml中的配置内容通过编程式的方式来实现。Spring充分利用了这一拓展,提供了ServletContainerInitializer接口的实现类SpringServletContainerInitializer来在启动的时候初始化所有的WebApplicationInitializer。

Spring Security使用了这个拓展方式,将请求通过tomcat Filter->spring filter->spring security filter这样的链路,转入到自己的处理逻辑当中。

对于我们自定义的过滤器也可以通过DelegatingFilterProxy作为接入点,将请求引入进来。

参考

  • servlet-spec-6.1

相关文章:

  • Oracle 的 PGA_AGGREGATE_LIMIT 参数
  • ElasticSearch 8.x 快速上手并了解核心概念
  • 养生指南:重塑健康生活的实用方案
  • 仿腾讯会议——添加音频
  • MySQL的锁机制
  • 电商后台管理系统:Django Admin深度定制实战指南
  • 蚂蚁数科的AI深潜与RWA远航
  • pinia.defineStore is not a function
  • NeRF适合口腔扫描仪场景吗
  • 深入理解pip:Python包管理的核心工具与实战指南
  • Go语言八股文之Mysql优化
  • 基于 STM32 的自动温度巡检小车控制系统设计与实现
  • ubuntu 安装 Redis新版Redis 7.x
  • vue3 vite 项目中自动导入图片
  • 从零训练一个大模型:DeepSeek 的技术路线与实践
  • windows网站篡改脚本编制
  • 若依框架二次开发——若依微服务整合RocketMQ
  • 三轴云台之高精度传感器与测距技术篇
  • java集合详细讲解
  • 《垒球百科全书》垒球是什么·棒球1号位
  • 台陆委会将欧阳娜娜等20多名艺人列入重要查核对象,国台办回应
  • 欧洲观察室|欧盟对华战略或在中欧建交50年时“低开高走”
  • 苏州1-4月进出口总值增长6.8%,工业机器人出口额倍增
  • 61岁云浮市律师协会副会长谭炳光因突发疾病逝世
  • 外交部部长助理兼礼宾司司长洪磊接受美国新任驻华大使递交国书副本
  • 刘小涛任江苏省委副书记