Spring Security原理分析【1】——整体布局切入
Spring Security 是Spring家族中基于JavaEE的企业Web应用程序的安全服务框架。准确而言是基于JavaEE中Servlet规范的Filter机制。
根据Servlet规范:一个客户端请求Request在Servlet容器中需要经过Filter Chain中一些列Filter处理后才会获取到Web资源,而且响应Response也需要再次经过Filter Chain中的Filter处理后才能返回给客户端。Spring基于该Servlet规范,在Filter中接入安全机制来保证Web资源的安全。
在Spring中,Filter对应的Bean为GenericFilterBean。它是一个抽象类,具体类为DelegatingFilterProxy,该类是一个委托类。
public class DelegatingFilterProxy extends GenericFilterBean {
...
// Web上下文
@Nullable
private WebApplicationContext webApplicationContext;
// 委托目标类名称
@Nullable
private String targetBeanName;
// 是否管理Filter的生命周期
private boolean targetFilterLifecycle = false;
// 委托目标类实例(Filter)
@Nullable
private volatile Filter delegate;
...
}
核心方法doFilter简化后如下:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// delegate支持懒加载
Filter delegateToUse = this.delegate;
// invokeDelegate 调用委托目标的doFilter
delegate.doFilter(request, response, filterChain);
}
正常情况下Servlet的Filter需要配置在web.xml中,这样Servlet容器初始化时就能找到并管理其生命周期,DelegatingFilterProxy则是Spring中用来找到那些不注册在web.xml中的Filter,然后交给Servlet去处理。
DelegatingFilterProxy是通过WebApplicationContext从IOC中根据targetBeanName获取已注册的Filter Bean。Spring Security基于Spring,因此也是利用该机制来完成Filter的注册的,其targetBeanName="springSecurityFilterChain"。
在基于web.xml类型的Web应用中,需要配置:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
基于JavaConfig类型的Web应用中,则需要配置:
@Bean
public FilterRegistrationBean springSecurityFilterChain(ServletContext servletContext) {
DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy("springSecurityFilterChain");
return new FilterRegistrationBean(springSecurityFilterChain);
}
springSecurityFilterChain这只是BeanName,其具体类为:FilterChainProxy,其doFilter如下:
@Override
public 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);
}
}
前面也说了一个客户端请求需要经过2次Filter,而整个请求是单条链,因此正常情况下只需要一次就可以了。Filter的核心为doFilter,在FilterChainProxy中抽象为doFilterInternal进行了隔离分层
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);
// 如果未获取到,则说明没有额外的过滤器注册,正常走Servlet的Filter流程
if (filters == null || filters.size() == 0) {
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
return;
}
// 如果获取到,则走Spring Security的过滤器执行流程,构建了VirtualFilterChain
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
// 虚拟的过滤器链执行
vfc.doFilter(fwRequest, fwResponse);
}
这里的getFilters很重要,决定了Spring Security到底执行了什么过滤器
private List<Filter> getFilters(HttpServletRequest request) {
// 对SecurityFilterChain逐个匹配HttpServletRequest
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
// 每条链是多个Filter,但是最终只返回首个匹配的链
return chain.getFilters();
}
}
return null;
}
SecurityFilterChain 是配置到FilterChainProxy中的,配置过程很复杂,这里先不说,最终配置好的SecurityFilterChain为一个集合:filterChains。而SecurityFilterChain#matches则决定了请求流程,这是一个单向开关,多个过滤器链最终只能执行一条。单条过滤器链抽象为VirtualFilterChain,该类是一个经典的变形责任链模式。
其实到这里,一个正常的Servlet容器已经将Spring Security容纳进去了:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!