一、SpringSecurity 的过滤器介绍

采用的是责任链的设计模式,它有一条很长的过滤器链。

  1. WebAsyncManagerIntegrationFilter:将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成。

  2. SecurityContextPersistenceFilter:在每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中,然后在该次请求处理完成之后,将SecurityContextHolder 中关于这次请求的信息存储到一个“仓储”中,然后将SecurityContextHolder 中的信息清除,例如在 Session 中维护一个用户的安全信息就是这个过滤器处理的。

  3. HeaderWriterFilter:用于将头信息加入响应中。

  4. CsrfFilter:用于处理跨站请求伪造。

  5. LogoutFilter:用于处理退出登录

  6. UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自 /login 的请求。从表单中获取用户名和密码时,默认使用的表单 name 值为 username 和 password,这两个值可以通过设置这个过滤器的 usernameParameter 和 passwordParameter 两个参数的值进行修改。

  7. DefaultLoginPageGeneratingFilter:如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面

  8. BasicAuthenticationFilter:检测和处理 http basic 认证。

  9. RequestCacheAwareFilter:用来处理请求的缓存

  10. SecurityContextHolderAwareRequestFilter:主要是包装请求对象 request

  11. AnonymousAuthenticationFilter:检测 SecurityContextHolder 中是否存在Authentication 对象,如果不存在为其提供一个匿名 Authentication。

  12. SessionManagementFilter管理 session 的过滤器

  13. ExceptionTranslationFilter:处理 AccessDeniedException 和AuthenticationException 异常。

  14. FilterSecurityInterceptor:可以看做过滤器链的出口。

  15. RememberMeAuthenticationFilter:当用户没有登录而直接访问资源时, 从 cookie 里找出用户的信息, 如果 Spring Security 能够识别出用户提供的 remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。

二、 SpringSecurity 基本流程

image-20211212184305204

绿色部分是认证过滤器,需要我们自己配置,可以配置多个认证过滤器。认证过滤器可以使用 Spring Security 提供的认证过滤器,也可以自定义过滤器(例如:短信验证)。认证过滤器要在 configure(HttpSecurity http)方法中配置,没有配置不生效。下面会重

点介绍以下三个过滤器:

  1. UsernamePasswordAuthenticationFilter 过滤器:该过滤器会拦截前端提交的 POST 方式的登录表单请求,并进行身份认证。
  2. ExceptionTranslationFilter 过滤器:该过滤器不需要我们配置,对于前端提交的请求会直接放行,捕获后续抛出的异常并进行处理(例如:权限访问限制)。
  3. FilterSecurityInterceptor 过滤器:该过滤器是过滤器链的最后一个过滤器,根据资源权限配置来判断当前请求是否有权限访问对应的资源。如果访问受限会抛出相关异常,并由 ExceptionTranslationFilter 过滤器进行捕获和处理。

三、 SpringSecurity 认证流程

认证流程是在 UsernamePasswordAuthenticationFilter 过滤器中处理的

image-20211212184702641

UsernamePasswordAuthenticationFilter 源码

当前端提交的是一个 POST 方式的登录表单请求,就会被该过滤器拦截,并进行身份认证。该过滤器的 doFilter() 方法实现在其抽象父类AbstractAuthenticationProcessingFilter 中,查看相关源码:

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
//过滤器doFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
if (!this.requiresAuthentication(request, response)) {
//1.判断是不是post提交,如果不是post提交直接放行,进入下一个过滤器
chain.doFilter(request, response);
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Request is to process authentication");
}
//Authentication用来存储用户信息的类
Authentication authResult;
try {
//2.调用子类重写的方法进行身份认证,返回Authentication中存储了用户的信息
authResult = this.attemptAuthentication(request, response);
if (authResult == null) {
return;
}
//3.Session策略处理(如果用户配置了Session最大并发数,就在此处理)
this.sessionStrategy.onAuthentication(authResult, request, response);
} catch (InternalAuthenticationServiceException var8) {
this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
//4.认证失败,调用认证失败的处理器
this.unsuccessfulAuthentication(request, response, var8);
return;
} catch (AuthenticationException var9) {
this.unsuccessfulAuthentication(request, response, var9);
return;
}
//4.认证成功的处理
if (this.continueChainBeforeSuccessfulAuthentication) {
//默认的值为false,认证成功之后不进入下一个处理器
chain.doFilter(request, response);
}
//认证成功的处理器
this.successfulAuthentication(request, response, chain, authResult);
}
}

上述的第二个过程调用子类UsernamePasswordAuthenticationFilter r 的attemptAuthentication() 方法

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
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private String usernameParameter = "username"; //默认表单用户名参数username
private String passwordParameter = "password"; //默认表单用户密码参数username
private boolean postOnly = true; //默认请求方式为POST
//默认登陆表单提交路径为/login POST方式提交
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}

//调用doFilter这里进行身份认证
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
//1.默认情况下如果请求不是POST会抛出异常
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
//2.获取请求的username和password
String username = this.obtainUsername(request);
String password = this.obtainPassword(request);
if (username == null) {
username = "";
}

if (password == null) {
password = "";
}

username = username.trim();
//3.使用前端传来的username、password构造Authentication对象,标记对象未认证
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
//4.将请求中一些属性放入设置到Authentication对象中(如remoteAddress、sessionId)
this.setDetails(request, authRequest);
//5.调用ProvideManger类的authenticate方法进行身份认证
return this.getAuthenticationManager().authenticate(authRequest);
}
}

上述的(3)过程创建的 UsernamePasswordAuthenticationToken 是Authentication 接口的实现类,该类有两个构造器,一个用于封装前端请求传入的未认证的用户信息,一个用于封装认证成功后的用户信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 520L;
private final Object principal;
private Object credentials;

//封装前端请求传的未认证的用户信息,前面的 authRequest对象就是调用该构造器进行构造的
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super((Collection)null); //用户权限为null
this.principal = principal; //前端传来的用户名
this.credentials = credentials; //前端传来的密码
this.setAuthenticated(false); //标记未认证
}

//用户封装认证成功后的用户信息
public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities); //用户权限列表
this.principal = principal; //封装认证用户信息的UserDetails对象,不是用户名
this.credentials = credentials; //前端传来的密码
super.setAuthenticated(true); //标记已认证
}

Authentication 接口的实现类用于存储用户认证信息,查看该接口具体定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface Authentication extends Principal, Serializable {
//用户权限集合
Collection<? extends GrantedAuthority> getAuthorities();
//用户密码
Object getCredentials();
//请求携带的一些信息sessionId,remoteAddress
Object getDetails();
//未认证时前端传来的用户名,认证成功后封装用户信息的UserDetails对象
Object getPrincipal();
//是否被认证
boolean isAuthenticated();
//设置是否被认证
void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

ProviderManager 源码

上述过程中,UsernamePasswordAuthenticationFilter 过滤器的attemptAuthentication() 方法的(5)过程将未认证的 Authentication 对象传入ProviderManager 类的 authenticate() 方法进行身份认证。

ProviderManagerAuthenticationManager 接口的实现类,该接口是认证相关的核心接口,也是认证的入口。在实际开发中,我们可能有多种不同的认证方式,例如:用户名+密码、邮箱+密码、手机号+验证码等,而这些认证方式的入口始终只有一个,那就是AuthenticationManager。在该接口的常用实现类 ProviderManager 内部会维护一个List列表,存放多种认证方式,实际上这是委托者模式(Delegate)的应用。每种认证方式对应着一个 AuthenticationProviderAuthenticationManager 根据认证方式的不同(根据传入的 Authentication 类型判断)委托对应的 AuthenticationProvider 进行用户认证。

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {

public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//(1)获取传入的Authentication类型,即UsernamePasswordAuthenticationToken.class
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
AuthenticationException parentException = null;
Authentication result = null;
Authentication parentResult = null;
boolean debug = logger.isDebugEnabled();
//(2)获取认证方式列表List<AuthenticationProvider>
Iterator var8 = this.getProviders().iterator();
//循环迭代
while(var8.hasNext()) {
AuthenticationProvider provider = (AuthenticationProvider)var8.next();
//(3)判断当前AuthenticationProvider是否适用UsernanePasSwordAuthenticationToken.class类型的﹔
if (provider.supports(toTest)) {
if (debug) {
logger.debug("Authentication attempt using " + provider.getClass().getName());
}
//成功找到适配当前认证方式的 AuthenticationProvider,此处为DaoAuthenticationProvider

try {
//(4)调用DaoAuthenticationProvider 的authenticate()方法进行认证;
//如果认证成功,会返回一个标记已认证的Authentication对象
result = provider.authenticate(authentication);
if (result != null) {
//(5)认证成功后,将传入的 Authentication对象中的details信息拷贝到已认证的 Authentication
this.copyDetails(authentication, result);
break;
}
} catch (InternalAuthenticationServiceException | AccountStatusException var13) {
this.prepareException(var13, authentication);
throw var13;
} catch (AuthenticationException var14) {
lastException = var14;
}
}
}

if (result == null && this.parent != null) {
//(5)认证失败,使用父类型AuthenticationManager进行验证
try {
result = parentResult = this.parent.authenticate(authentication);
} catch (ProviderNotFoundException var11) {
} catch (AuthenticationException var12) {
parentException = var12;
lastException = var12;
}
}

if (result != null) {
//(6)认证成功之后,去除result 的敏感信息,要求相关类实现CredentialsContainer接口
if (this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) {
((CredentialsContainer)result).eraseCredentials();
}
//(7)发布认证成功的事件
if (parentResult == null) {
this.eventPublisher.publishAuthenticationSuccess(result);
}

return result;
} else {
//(8)认证失败之后抛出失败的异常
if (lastException == null) {
lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[]{toTest.getName()}, "No AuthenticationProvider found for {0}"));
}

if (parentException == null) {
this.prepareException((AuthenticationException)lastException, authentication);
}

throw lastException;
}
}
}

上述认证成功之后的(6)过程,调用 CredentialsContainer 接口定义的eraseCredentials() 方法去除敏感信息。查看UsernamePasswordAuthenticationToken 实现的 eraseCredentials() 方法,该方法实现在其父类中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
public void eraseCredentials() {
//credentials(前端传入的密码)会置为null
this.eraseSecret(this.getCredentials());
//principal在已认证的 Authentication中是 UserDetails 实现类;如果该实现类想要去除敏感信息,需要实现CredentialsContainer接口的eraseCredentials()方法;由于我们自定义的 User类没有实现该接口,所以不进行任何操作。
this.eraseSecret(this.getPrincipal());
this.eraseSecret(this.details);
}

private void eraseSecret(Object secret) {
if (secret instanceof CredentialsContainer) {
((CredentialsContainer)secret).eraseCredentials();
}

}

认证成功/失败处理

UsernamePasswordAuthenticationFilter 过滤器的 doFilter() 方法,查看认证成功/失败的处理

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
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {
//认证成功的处理
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
//1.将认证成功的用户信息Authentication封装进SecurityContext对象中,并存入SecurityContextHolder,securitycontextHolder是对ThreadLocal的一个封装,后续会介绍
SecurityContextHolder.getContext().setAuthentication(authResult);
//2.rememberMe处理
this.rememberMeServices.loginSuccess(request, response, authResult);
if (this.eventPublisher != null) {
//3.发布认证成功的事件
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
//调用认证成功的处理器
this.successHandler.onAuthenticationSuccess(request, response, authResult);
}

//认证失败的处理
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
//1.清除该线程在SecurityContextHolder对应的SecurityContext对象
SecurityContextHolder.clearContext();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Authentication request failed: " + failed.toString(), failed);
this.logger.debug("Updated SecurityContextHolder to contain null Authentication");
this.logger.debug("Delegating to authentication failure handler " + this.failureHandler);
}
//2.rememberMe处理
this.rememberMeServices.loginFail(request, response);
//3.调用认证失败的处理器
this.failureHandler.onAuthenticationFailure(request, response, failed);
}

image-20211214231203150

四、SpringSecurity权限流程

绍权限访问流程,主要是对ExceptionTranslationFilter 过滤器和 FilterSecurityInterceptor 过滤器进行介绍。

ExceptionTranslationFilter

是用于处理异常的,不需要我们配置,对于前端提交的请求会直接放行,捕获后续抛出的异常并进行处理(例如:权限访问限制)。具体源码如下:

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
//处理AccessDeniedException和AuthenticationException
public class ExceptionTranslationFilter extends GenericFilterBean {
...

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

try {
//1.
chain.doFilter(request, response);
this.logger.debug("Chain processed normally");
} catch (IOException var9) {
throw var9;
} catch (Exception var10) {
//2.捕获后序出现的异常进行处理
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var10);
//访问需要认证的资源,但当前请求未认证抛出的异常
RuntimeException ase = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (ase == null) {
//权限受限的资源所抛出的异常
ase = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}

if (ase == null) {
if (var10 instanceof ServletException) {
throw (ServletException)var10;
}

if (var10 instanceof RuntimeException) {
throw (RuntimeException)var10;
}

throw new RuntimeException(var10);
}

if (response.isCommitted()) {
throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", var10);
}

this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
}

}

}

FilterSecurityInterceptor

FilterSecurityInterceptor 是过滤器链的最后一个过滤器,该过滤器是过滤器链的最后一个过滤器,根据资源权限配置来判断当前请求是否有权限访问对应的资源。如果访问受限会抛出相关异常,最终所抛出的异常会由前一个过滤器ExceptionTranslationFilter 进行捕获和处理。

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
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
//调用invoke方法
invoke(fi);
}

public void invoke(FilterInvocation fi) throws IOException, ServletException {
if ((fi.getRequest() != null)
&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
&& observeOncePerRequest) {
// filter already applied to this request and user wants us to observe
// once-per-request handling, so don't re-do security checking
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
else {
// first time this request being called, so perform security checking
if (fi.getRequest() != null && observeOncePerRequest) {
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
}

//1.根据资源权限配置判断当前请求是否有权限访问对应资源,如果不能访问则抛出异常
InterceptorStatusToken token = super.beforeInvocation(fi);

try {
//2.访问相关资源,通过SpringMVC核心组件DispatcherServlet进行访问
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
finally {
super.finallyInvocation(token);
}

super.afterInvocation(token, null);
}
}
}

需要注意,Spring Security 的过滤器链是配置在 SpringMVC 的核心组件DispatcherServlet 运行之前。也就是说,请求通过 Spring Security 的所有过滤器,不意味着能够正常访问资源,该请求还需要通过 SpringMVC 的拦截器链。

五、SpringSecurity 请求间共享认证信息

一般认证成功后的用户信息是通过 Session 在多个请求之间共享,那么 Spring Security 中是如何实现将已认证的用户信息对象 Authentication 与 Session 绑定的进行具体分析

image-20211214233009749
  • 认证成功的处理方法 successfulAuthentication()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {

if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
//将以认证的信息对象封装到SecurityContext中,存入SecurityContextHolder中
SecurityContextHolder.getContext().setAuthentication(authResult);

rememberMeServices.loginSuccess(request, response, authResult);

// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}

successHandler.onAuthenticationSuccess(request, response, authResult);
}

查 看 SecurityContext 接 口 及 其 实 现 类 SecurityContextImpl , 该 类 其 实 就 是 对Authentication 的封装:

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
public interface SecurityContext extends Serializable {
Authentication getAuthentication();

void setAuthentication(Authentication var1);
}

public class SecurityContextImpl implements SecurityContext {
private static final long serialVersionUID = 520L;
private Authentication authentication;

public SecurityContextImpl() {
}

public SecurityContextImpl(Authentication authentication) {
this.authentication = authentication;
}

public boolean equals(Object obj) {
if (obj instanceof SecurityContextImpl) {
SecurityContextImpl test = (SecurityContextImpl)obj;
if (this.getAuthentication() == null && test.getAuthentication() == null) {
return true;
}

if (this.getAuthentication() != null && test.getAuthentication() != null && this.getAuthentication().equals(test.getAuthentication())) {
return true;
}
}

return false;
}

public Authentication getAuthentication() {
return this.authentication;
}

public int hashCode() {
return this.authentication == null ? -1 : this.authentication.hashCode();
}

public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}

public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
if (this.authentication == null) {
sb.append(": Null authentication");
} else {
sb.append(": Authentication: ").append(this.authentication);
}

return sb.toString();
}
}

查 看 SecurityContextHolder 类 , 该 类 其 实 是 对 ThreadLocal 的 封 装 , 存 储SecurityContext 对象:

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
public class SecurityContextHolder {
private static SecurityContextHolderStrategy strategy;
private static int initializeCount = 0;
public SecurityContextHolder() {
}
private static void initialize() {
if (!StringUtils.hasText(strategyName)) {
//默认使用MODE_THREADLOCAL模式
strategyName = "MODE_THREADLOCAL";
}

if (strategyName.equals("MODE_THREADLOCAL")) {
//默认使用ThreadLocalSecur1tyContextHolderStrategy 创建 strategy,其内部使用ThreadLocal对SecurltyContext进行存储
strategy = new ThreadLocalSecurityContextHolderStrategy();
} else if (strategyName.equals("MODE_INHERITABLETHREADLOCAL")) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
} else if (strategyName.equals("MODE_GLOBAL")) {
strategy = new GlobalSecurityContextHolderStrategy();
} else {
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy)customStrategy.newInstance();
} catch (Exception var2) {
ReflectionUtils.handleReflectionException(var2);
}
}

++initializeCount;
}
public static SecurityContext getContext() {
//需要注意,如果当前线程对应的ThreadLocal<SecurityContext>没有任何对象存储strategy.getContext()会创建并返回一个空的SecurityContext对象,并且该空的SecurityContext对象会存入ThreadLocal<Securitycontext>
return strategy.getContext();
}

public static void setContext(SecurityContext context) {
strategy.setContext(context);
}

}
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
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
//使用ThreadLocal对SecurltyContext进行存储
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal();

ThreadLocalSecurityContextHolderStrategy() {
}

public void clearContext() {
contextHolder.remove();
}

public SecurityContext getContext() {
//需要注意,如果当前线程对应的ThreadLocal<SecurityContext>没有任何对象存储strategy.getContext()会创建并返回一个空的SecurityContext对象,并且该空的SecurityContext对象会存入ThreadLocal<Securitycontext>
SecurityContext ctx = (SecurityContext)contextHolder.get();
if (ctx == null) {
ctx = this.createEmptyContext();
contextHolder.set(ctx);
}

return ctx;
}

public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder.set(context);
}

public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}

SecurityContextPersistenceFilter 过滤器

UsernamePasswordAuthenticationFilter 过滤器认证成功之后,会在认证成功的处理方法中将已认证的用户信息对象 Authentication 封装进SecurityContext,并存入 SecurityContextHolder

之后,响应会通过 SecurityContextPersistenceFilter 过滤器,该过滤器的位置在所有过滤器的最前面,请求到来先进它,响应返回最后一个通过它,所以在该过滤器中处理已认证的用户信息对象 Authentication 与 Session 绑定。

认证成功的响应通过 SecurityContextPersistenceFilter 过滤器时,会从SecurityContextHolder 中取出封装了已认证用户信息对象 Authentication 的SecurityContext,放进 Session 中。当请求再次到来时,请求首先经过该过滤器,该过滤器会判断当前请求的 Session 是否存有 SecurityContext 对象,如果有则将该对象取出再次放入 SecurityContextHolder 中,之后该请求所在的线程获得认证用户信息,后续的资源访问不需要进行身份认证;当响应再次返回时,该过滤器同样从 SecurityContextHolder 取出SecurityContext 对象,放入 Session 中。

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
57
58
59
60
61
public class SecurityContextPersistenceFilter extends GenericFilterBean {

...

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

if (request.getAttribute(FILTER_APPLIED) != null) {
// ensure that filter is only applied once per request
chain.doFilter(request, response);
return;
}

final boolean debug = logger.isDebugEnabled();

request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

if (forceEagerSessionCreation) {
HttpSession session = request.getSession();

if (debug && session.isNew()) {
logger.debug("Eagerly created session: " + session.getId());
}
}
//1.请求到来时,检查当前Session中是否存有SecurityContext对象,如果有,从Session中取出该对象;如果没有,创建一个空的 SecurityContext对象
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
response);
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

try {
//2.将上述获得SecurityContext对象放入SecurityconteztHolder中
SecurityContextHolder.setContext(contextBeforeChainExecution);
//3.进入下一个过滤器
chain.doFilter(holder.getRequest(), holder.getResponse());

}
finally {
//4.响应返回时,从SecurityContextHolder中取出securityContext
SecurityContext contextAfterChainExecution = SecurityContextHolder
.getContext();
// Crucial removal of SecurityContextHolder contents - do this before anything
// else.
//移除SecuritycontextHolder中的SecurityContext对象
SecurityContextHolder.clearContext();
//将取出的SecurityContext 对象放进session
repo.saveContext(contextAfterChainExecution, holder.getRequest(),
holder.getResponse());
request.removeAttribute(FILTER_APPLIED);

if (debug) {
logger.debug("SecurityContextHolder now cleared, as request processing completed");
}
}
}

public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
this.forceEagerSessionCreation = forceEagerSessionCreation;
}
}