OUZHANBO

对于我这种菜鸡来说,毕业等于失业

0%

SpringSecurity中的SecurityContextHolder分析

SpringSecurity 中的 SecurityContextHolder 分析

分析

一般我们在 SpringSecurity 中可以通过 SecurityContextHolder.getContext().getAuthentication()获取到认证信息,这个认证信息就是存放在 SecurityContext 中,这个 SecurityContext 就是通过 SecurityContextHolder.getContext()获取到的,SecurityContextHolder.getContext()的代码如下

1
2
3
public static SecurityContext getContext() {
return strategy.getContext();
}

上面的这个 strategy 是根据具体的策略来确定到底从哪获取 SecurityContext,这个 strategy 的初始化代码

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
static {
initialize();
}

private static void initialize() {
if (!StringUtils.hasText(strategyName)) {
// Set default
//默认的策略是从ThreadLocal中获取,可以通过在SecurityContextHolder.setStrategyName()方法设置
strategyName = MODE_THREADLOCAL;
}
//默认的策略是从ThreadLocal中获取
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
}
//可继承的ThreadLocal
else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
}
//全局模式
else if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
}
else {
// Try to load a custom strategy
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}

initializeCount++;
}

ThreadLocal 模式下的使用的是 ThreadLocalSecurityContextHolderStrategy 这个类的对象来存储 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
final class ThreadLocalSecurityContextHolderStrategy implements
SecurityContextHolderStrategy {
// ~ Static fields/initializers
// =====================================================================================

//使用ThreadLocal来存储,在同一个线程中可以获取得到(不了解ThredLocal的使用可以上网查一下)
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();

// ~ Methods
// ========================================================================================================

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

public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();

if (ctx == null) {
ctx = 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();
}
}

接下来再看下全局模式对应的 GlobalSecurityContextHolderStrategy 的代码

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
final class GlobalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
// ~ Static fields/initializers
// =====================================================================================
//直接使用静态变量存储,所有线程获取到的都是同一个东西
private static SecurityContext contextHolder;

// ~ Methods
// ========================================================================================================

public void clearContext() {
contextHolder = null;
}

public SecurityContext getContext() {
if (contextHolder == null) {
contextHolder = new SecurityContextImpl();
}

return contextHolder;
}

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

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

那个可继承 ThreadLocal 用得比较少,应该和和上面的 ThradLocal 差不多,感兴趣的可以去网上查一下,这里就不分析了

总结

  • SecurityContextHolder 的作用是保存 SecurityContext
  • SecurityContextHolder 默认使用的是 ThreadLocal 模式来保存 SecurityContext,在这种模式下同一个线程获取到 SecurityContext 都是一样的,如果是全局模式下,所有的线程获取到的 SecurityContext 都是同一个,如果需要使用其它模式可以通过调用 SecurityContextHolder.setStrategyName 方法来设置