盘点认证框架 SpringSecurity Filter

总文档 :文章目录

Github : github.com/black-ant

一 . 前言

上一篇聊了聊 Secutity 的基础 , 这一篇我们聊一聊 Securiy Filter , 基本上 Security 常见得功能都能通过 Filter 找到相关的痕迹 .

二 . 一个基本的 Filter 案例

先来看看我们之前这么注册 Filter 的 >>>

1
2
3
java复制代码AbstractAuthenticationProcessingFilter filter = new DatabaseAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);

我们来追溯一下 Filter 怎么被加载进去的 :

Step First : 将 Filter 加载到 Security 体系中

1
2
3
4
5
6
7
8
9
java复制代码// 添加到 HttpSecurity
C- HttpSecurity
- this.filters.add(filter) : 这里的 filters 仅仅是一个 List 集合

// 追溯代码可以看到这个集合会用于创建一个 DefaultSecurityFilterChain
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(this.filters, this.comparator);
return new DefaultSecurityFilterChain(this.requestMatcher, this.filters);
}

HttpSecurity 这个类我们后面会详细说说 , 这里了解到他其中维护了一个 Filter 集合即可 , 这个集合会被加载到 FilterChain 中

Step 2 : Filter Chain 的使用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
java复制代码// 成功标注断点后 , 可以追溯到整个的加载流程 : 
// Step 1 : 要构建一个 springSecurityFilterChain
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain()throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {});
webSecurity.apply(adapter);
}
return webSecurity.build();
}

// Step 2 : webSecurity.build() 执行构建
public final O build() throws Exception {
// 居然还可以看到 CAS 操作 , 这里应该是设置绑定状态
if (this.building.compareAndSet(false, true)) {
// 执行 Build
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}

// Step 3 : AbstractConfiguredSecurityBuilder 中
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
// 为子类挂载钩子
beforeInit();
init();

buildState = BuildState.CONFIGURING;
// 在调用每个SecurityConfigurer#configure(SecurityBuilder)方法之前调用。
// 子类可以在不需要使用SecurityConfigurer时 , 覆盖这个方法来挂载到生命周期中
beforeConfigure();
configure();

buildState = BuildState.BUILDING;
// 实际构建对象
O result = performBuild();

buildState = BuildState.BUILT;

return result;
}
}


// Step 4 : 至此反射获取到 Filters 链
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}

image.png

可以看到 , 最开始添加到 Filter 集合的 Filter ,最终会用于构建 springSecurityFilterChain , 那么 springSecurityFilterChain 又是干什么的呢?

三 . Security Filter Chain

Security 的 Filter 和 WebFilter 本质是一样的 , 只是为了实现 Seccurity 的功能

>>> 来看一下 FilterChain 的调用链 :

FilterChain001.jpg

image.png

3.1 FilterChain 的创建过程

FilterChain 的核心是一个 VirtualFilterChain , 每个请求过来都会有一个VirtualFilterChain 生成

VirtualFilterChain 是 FilterChainProxy 的内部类.

1
2
3
4
5
6
7
8
9
10
java复制代码// 补充 : VirtualFilterChain : 内部过滤器链实现,用于通过与请求匹配的额外内部过滤器列表传递请求
C- VirtualFilterChain
?- 每次运行的时候都会创建 , 来链表调用所有的 Filter
P- currentPosition : 当前运行 Filter 的下标
P- FirewalledRequest : 可用于拒绝潜在危险的请求和/或包装它们来控制它们的行为
P- List<Filter> additionalFilters : 包含所有的 Filter 对象
M- doFilter(ServletRequest request, ServletResponse response)
?- 这个方法会从2个维度来处理
1- currentPosition == size : 当执行最后一个的时候 , 先重置 FirewalledRequest , 再调用 originalChain
2- currentPosition != size : 在此之前依次执行 Filter 集合中的 doFilter

VirtualFilterChain 的创建流程 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
java复制代码
C- FilterChainProxy extends GenericFilterBean
?- GenericFilterBean 继承了 Filter 接口 , 其最终会由 SpringWeb 的 Filter 进行调用
M- doFilterInternal
- FirewalledRequest 的相关处理
- 创建了一个 VirtualFilterChain ,执行 Filter 链

// implements Filter
@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);
}
}



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) {
// 日志略...
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
return;
}
// 这里创建了一个内部类 VirtualFilterChain
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
// 类似于链表的方式 , 依次来调用
vfc.doFilter(fwRequest, fwResponse);
}

// 扩展 : FirewalledRequest
C- FirewalledRequest :
?- 这是一个配合防火墙功能的Request 实现类 , 其通常配合 HttpFirewall 来实现
M- reset : 重置方法,该方法允许在请求离开安全过滤器链时由FilterChainProxy重置部分或全部状态

C- StrictHttpFirewall
M- getFirewalledRequest
- rejectForbiddenHttpMethod(request) : 拒绝禁止的 HttpMethod
- rejectedBlacklistedUrls(request) : 拒绝黑名单 URL
- return new FirewalledRequest(request) : 这里创建了一个 FirewalledRequest , 不过 reset 是空实现

可以看到 , 这里每次执行 doFilterInternal 时都会创建一个 VirtualFilterChain .

主要抽象类 AbstractAuthenticationProcessingFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码C- AbstractAuthenticationProcessingFilter 
- !requiresAuthentication(request, response) : 确定是否匹配该请求
?- 注意 ,我们构建 DatabaseAuthenticationFilter 的时候其实是传入了一个Matcher匹配器的
- requiresAuthenticationRequestMatcher.matches(request);
- 不匹配则继续执行 FilterChain
- 匹配后继续执行
- Authentication authResult = attemptAuthentication(request, response);
// 这个方法是需要实现类复写的 , 在实现类中我们做了下面的事情
- 将 Request 中的验证信息 (账户密码, 如果需要扩展 ,可以是更多信息 , Cookie , Header 等等) 取出
- 构建了一个 Token (DatabaseUserToken)
- 将 Token 放入 Details 中
- 通过 AuthenticationManager 调用 ProviderManager 完成认证
// 具体的认证方式我们后续在详细分析

我之前以为 Security 的方式是把 所有的 Filter 走一遍后再执行 Provider , 从这里看来他采用的是Filter 适配后就直接执行 Provider

3.2 WebAsyncManagerIntegrationFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
java复制代码
C- WebAsyncManagerIntegrationFilter
?- 提供SecurityContext和Spring Web的webbasyncmanager之间的集成
?- SecurityContextCallableProcessingInterceptor#beforeConcurrentHandling 用于填充SecurityContext

C- 创建一个WebAsyncManager
?- 用于管理异步请求处理的中心类,主要用作SPI,通常不直接由应用程序类使用。

@Override
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {
// 创建一个WebAsyncManager
// 用于管理异步请求处理的中心类,主要用作SPI,通常不直接由应用程序类使用。
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor) asyncManager
.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
// 如果没有 SecurityContextCallableProcessingInterceptor , 则创建一个注入 WebAsyncManager
if (securityProcessingInterceptor == null) {
asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY,new SecurityContextCallableProcessingInterceptor());
}

filterChain.doFilter(request, response);
}

3.3 SecurityContextPersistenceFilter 体系

注意 , SecurityContext 是整个认证的核心 , 拥有 SecurityContext 即表示认证成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java复制代码C- SecurityContextPersistenceFilter
?- 这是一个必选的Filter , 其目的是为了往 SecurityContextHolder 中插入一个 SecurityContext , SecurityContext 是最核心的认证容器
- SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
?- 注意 , 这里会尝试获取Sesssion 的 Context , 用于验证
- SecurityContextHolder.setContext(contextBeforeChainExecution)
?- 设置一个 Context
- chain.doFilter(holder.getRequest(), holder.getResponse());
- finally 中会在所有filter 完成后 , 往 SecurityContextHolder 插入一个 contextAfterChainExecution
?- 注意前面是 contextBeforeChainExecution

// finally 代码一览
finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
// 清除 Context
SecurityContextHolder.clearContext();
// 重新保存新得 Context
repo.saveContext(contextAfterChainExecution, holder.getRequest(),holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
}

3.4 HeaderWriterFilter 体系

List<HeaderWriter> headerWriters : 构造对象的时候会写入一个list

核心方法 : doFilterInternal

  • 准备了2个 HeaderWriterResponse , HeaderWriterRequest , 他们支持对Header 的二次封装 (原本的 Servlet 是不支持的)
  • filterChain 完成后会执行 headerWriterResponse.writeHeaders();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码// eaderWriterResponse.writeHeaders() 是从 HeadersConfigurer 中获取的
C- HeadersConfigurer
M- private List<HeaderWriter> getHeaderWriters() {
List<HeaderWriter> writers = new ArrayList<>();
addIfNotNull(writers, contentTypeOptions.writer);
addIfNotNull(writers, xssProtection.writer);
addIfNotNull(writers, cacheControl.writer);
addIfNotNull(writers, hsts.writer);
addIfNotNull(writers, frameOptions.writer);
addIfNotNull(writers, hpkp.writer);
addIfNotNull(writers, contentSecurityPolicy.writer);
addIfNotNull(writers, referrerPolicy.writer);
addIfNotNull(writers, featurePolicy.writer);
writers.addAll(headerWriters);
return writers;
}

3.5 LogoutFilter

LogoutFilter 允许定制LogoutHandler , 这一点在构造函数里面就能看到
可以看到 , 默认使用 logout 地址作为拦截请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
java复制代码public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler,
LogoutHandler... handlers) {
this.handler = new CompositeLogoutHandler(handlers);
Assert.notNull(logoutSuccessHandler, "logoutSuccessHandler cannot be null");
this.logoutSuccessHandler = logoutSuccessHandler;
setFilterProcessesUrl("/logout");
}


// LogoutFilter doFilter 逻辑
M- doFilter
?- 只要的操作就是调用handler 执行 logout 逻辑 , 并且调用 LogoutSuccess 逻辑

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;

if (requiresLogout(request, response)) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
// 执行 LogoutHandler 的实现类
this.handler.logout(request, response, auth);
// 执行 LogoutSuccessHandler 的实现类
logoutSuccessHandler.onLogoutSuccess(request, response, auth);
return;
}

chain.doFilter(request, response);
}

// PS : 这个 Filter 有一定局限性 , 无法处理多个 Handler , 可以考虑定制一个 Filter
// Handler 实现类我们后续再深入

3.6 CsrfFilter

之前了解到 , 为了使同步器令牌模式能够防止 CSRF 攻击,必须在 HTTP 请求中包含实际的 CSRF 令牌。这必须包含在浏览器不会自动包含在 HTTP 请求中的请求的一部分(即表单参数、 HTTP 头等)中。

Spring Security 的 CsrfFilter 将一个 CsrfToken 作为一个名为 _csrf 的 HttpServletRequest 公开属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
java复制代码
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(HttpServletResponse.class.getName(), response);
// 加载 CsrfToken
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
final boolean missingToken = csrfToken == null;
if (missingToken) {
// 缺失则重新创建一个
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
request.setAttribute(csrfToken.getParameterName(), csrfToken);

if (!this.requireCsrfProtectionMatcher.matches(request)) {
filterChain.doFilter(request, response);
return;
}

// 跨域后比对实际Token
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
if (!csrfToken.getToken().equals(actualToken)) {
if (missingToken) {
this.accessDeniedHandler.handle(request, response,new MissingCsrfTokenException(actualToken));
} else {
this.accessDeniedHandler.handle(request, response,new InvalidCsrfTokenException(csrfToken, actualToken));
}
return;
}

filterChain.doFilter(request, response);
}

3.9 其他零散Token

如果缓存的请求与当前请求匹配,则负责重新构造已保存的请求

整个核心代码主要是 2句话:其中主要是封装了一个新得 wrappedSavedRequest

1
2
3
java复制代码HttpServletRequest wrappedSavedRequest = requestCache.getMatchingRequest(
(HttpServletRequest) request, (HttpServletResponse) response);
chain.doFilter(wrappedSavedRequest == null ? request : wrappedSavedRequest,response);

SecurityContextHolderAwareRequestFilter
一个过滤器,它使用实现servlet API安全方法的请求包装器填充ServletRequest

简单点说 , 就是一个封装 Request 的 Filter , 封装的 HttpServletRequest 提供了很多额外的功能

  • HttpServletRequest.authenticate() - 允许用户确定他们是否被验证,如果没有,则将用户发送到登录页面
  • HttpServletRequest.login() - 允许用户使用AuthenticationManager进行身份验证
  • HttpServletRequest.logout() - 允许用户使用Spring Security中配置的LogoutHandlers注销

SessionManagementFilter
SessionManagementFilter 中提供了多个对象用于在用户已经认证后进行 Session 会话活动 ,** 激活会话固定保护机制或检查多个并发登录**

  • SecurityContextRepository securityContextRepository;
  • SessionAuthenticationStrategy sessionAuthenticationStrategy;

3.8 ExceptionTranslationFilter

作用 : 处理过滤器链中抛出的任何AccessDeniedException和AuthenticationException。

如果检测到AuthenticationException,过滤器将启动authenticationEntryPoint。这允许通用地处理来自AbstractSecurityInterceptor的任何子类的身份验证失败。

sendStartAuthentication(request, response, chain,(AuthenticationException) exception);

如果检测到AccessDeniedException,筛选器将确定该用户是否是匿名用户

  • 如果它们是匿名用户,则将启动authenticationEntryPoint。
  • 如果它们不是匿名用户,则筛选器将委托给AccessDeniedHandler。
  • 默认情况下,过滤器将使用AccessDeniedHandlerImpl。
1
2
java复制代码sendStartAuthentication(request,response,chain,new InsufficientAuthenticationException(
messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication","Full authentication is required to access this resource")));

(PS : 因为是链式结构 , 所以他作为最后一个 , 也是处在最外层的)

核心是通过一个 catch 来处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java复制代码try {
chain.doFilter(request, response);
}catch (Exception ex) {
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
RuntimeException ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
// 获取依次类型
if (ase == null) {
ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(
AccessDeniedException.class, causeChain);
}

if (ase != null) {
if (response.isCommitted()) {
throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", ex);
}
// 专属处理 SpringSecurityException
handleSpringSecurityException(request, response, chain, ase);
}else {
if (ex instanceof ServletException) {
throw (ServletException) ex;
}else if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
throw new RuntimeException(ex);
}
}
  • 首先,ExceptionTranslationFilter 调用 FilterChain.doFilter (请求、响应)来调用应用程序的其余部分。
  • 如果用户没有经过身份验证,或者它是 AuthenticationException,那么启动身份验证。
    • 清空 Security contextholder
    • 将 HttpServletRequest 保存在 RequestCache 中。当用户成功进行身份验证时,使用 RequestCache获取原始请求
    • AuthenticationEntryPoint 用于从客户机请求凭据。
      • 例如,它可能会重定向到一个登录页面,或者发送一个 WWW-Authenticate 标头。
  • 否则,如果它是一个 AccessDeniedException,那么 Access Denied

image.png

如果应用程序没有抛出 AccessDeniedException 或 AuthenticationException,那么 ExceptionTranslationFilter 不会做任何事情。

四 . 业务流

谈到了 Filter , 肯定就要细聊 Filter 对应的业务 , 上面说了一些简单的 Filter 业务 , 这一段我们来说一说比较大的业务流程 :

4.1 HttpSecurity 的业务匹配

我们在配置 Security 的时候 , 一般都会配置 Request Match 等参数 , 例如 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码 http.authorizeRequests()
.antMatchers("/test/**").permitAll()
.antMatchers("/before/**").permitAll()
.antMatchers("/index").permitAll()
.antMatchers("/").permitAll()
.anyRequest().authenticated() //其它请求都需要校验才能访问
.and()
.formLogin()
.loginPage("/login") //定义登录的页面"/login",允许访问
.defaultSuccessUrl("/home") //登录成功后默认跳转到"list"
.successHandler(myAuthenticationSuccessHandler).failureHandler(myAuthenctiationFailureHandler).permitAll().and()
.logout() //默认的"/logout", 允许访问
.logoutSuccessUrl("/index")
.permitAll();

那么这些参数是怎么生效的呢 ?

Step End : 最终匹配对象

我们来根据整个业务流程逆推 , 其最终对象是一个 RequestMatcher 实现类

注意 , 我们其上的 antMatchers 类型会生成多种不同的实现类 :

  • AndRequestMatcher : 请求路径
  • IpAddressMatcher : IP地址
  • MediaTypeRequestMatcher : 媒体类型

…. 等等其他的就不详细说了

拿到实现类后 ,调用 实现类的matches 方法返回最终结果

1
2
3
java复制代码public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}

Step Start : 看看请求的起点

找到了最终的匹配点 , 后面就好说了 , 打个断点 , 整个调用链就清清楚楚了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
java复制代码

// Step1 : FilterChainProxy
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);

//...... 省略
// 执行 Filter 链 , 如果没有相当于直接进去
if (filters == null || filters.size() == 0) {
chain.doFilter(fwRequest, fwResponse);
}

// Step2 : getFilters 过滤 Filter Chain
for (SecurityFilterChain chain : filterChains) {
// 如果地址匹配 , 则执行对象 Filter 链
if (chain.matches(request)) {
return chain.getFilters();
}
}

这里可以看到 , 如果没有被拦截成功的 ,最终应该就直接运行了 , 所以 Security 一切的起点都是 Filter

五.补充

5.1 DelegatingFilterProxy 补充

Security 通过 DelegatingFilterProxy 将 Security 融入到 WebFilter 的体系中 ,其主要流程为 :

  • C- DelegatingFilterProxy # doFilter
  • C- DelegatingFilterProxy # invokeDelegate
  • C- FilterChainProxy # doFilter

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
JAVA复制代码public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

// -> PIC51001 : delegate 对象结构
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("...");
}
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}

// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}



protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

// 会调用到 FilterChainProxy , 正式进入 Security 体系
delegate.doFilter(request, response, filterChain);
}

PIC51001 : delegate 对象结构

image.png

继续补充 : delegate 的初始化 , 获取 FilterChain

1
2
3
4
5
6
7
8
9
java复制代码protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = getTargetBeanName();
// 这里的 TargetBeanName 为 springSecurityFilterChain
Filter delegate = wac.getBean(targetBeanName, Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}

总结

Spring Security 的起点就是 Filter ,常用的功能都能通过 Filter 找到相关的痕迹 , 后续我们会继续分析更底层的东西, 来开枝散叶的看看底下经历了什么

Security 的 Filter 和 WebFilter 的本质一样 , Security 通过一个 DelegatingFilterProxySecurityFilterChain 集中到 Filter 体系中

FilterChain 的核心是一个 VirtualFilterChain , 每个请求过来都会有一个VirtualFilterChain 生成 ,其中会添加所有的 Filter 类

的 Filter 包括 :

  • SecurityContextPersistenceFilter : 对 SecurityContext 进行持久化操作
  • HeaderWriterFilter : 对 Header 进行二次处理 , 因为很多认证信息会放在 Header 中 , 这也是一个极其重要的类
  • LogoutFilter : 拦截 logout 请求 , 并且退出
  • ExceptionTranslationFilter : 对流程中的异常进行处理 (AccessDeniedException和AuthenticationException)

Filter 会通过 matches 进行拦截 , 判断是否要执行 Filters 逻辑

更新日志

V20210803 : 补充 DelegatingFilterProxy 逻辑

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%