本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰前端
springsecurity中,用戶登陸信息本質是保存到HttpSession中,springsecurity進行封裝 獲取登陸數據有兩種思路:java
@RestController
public class HelloController {
@GetMapping("/hello")
public void hello() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println("authentication.getClass() = " + authentication.getClass());
}
}
複製代碼
SecurityContextHolder存放的是SecurityContext ,SecurityContextHolder中定義三種不一樣的數據存儲策略,採用了策略模式web
SecurityContextHolderStrategy接口用來規範存儲策略中的方法spring
public interface SecurityContextHolderStrategy {
void clearContext();
SecurityContext getContext();
void setContext(SecurityContext var1);
SecurityContext createEmptyContext();
}
複製代碼
有三個實現類 對應三個不一樣的存儲策略後端
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.security.core.context;
import org.springframework.util.Assert;
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal();
ThreadLocalSecurityContextHolderStrategy() {
}
public void clearContext() {
contextHolder.remove();
}
public SecurityContext getContext() {
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();
}
}
複製代碼
存儲載體是ThreadLocal 針對SecurityContext的操做都是在ThreadLocal中進行操做。SecurityContext只是個接口,只有一個實現類是SecurityContextImplmarkdown
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.security.core.context;
import org.springframework.util.Assert;
final class InheritableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<SecurityContext> contextHolder = new InheritableThreadLocal();
InheritableThreadLocalSecurityContextHolderStrategy() {
}
public void clearContext() {
contextHolder.remove();
}
public SecurityContext getContext() {
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();
}
}
複製代碼
存儲載體爲InheritableThreadLocal ,InheritableThreadLocal繼承ThreadLocal,多了一個特性,就是在子線程建立的時間,會自動將父線程的數據複製到子線程中。實現了子線程中可以獲取登陸數據的功能。多線程
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.security.core.context;
import org.springframework.util.Assert;
final class GlobalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static SecurityContext contextHolder;
GlobalSecurityContextHolderStrategy() {
}
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();
}
}
複製代碼
存儲載體是一個靜態變量,也能夠在多線程環境下使用,但用的較少。app
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.security.core.context;
import java.lang.reflect.Constructor;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
public class SecurityContextHolder {
public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
public static final String MODE_GLOBAL = "MODE_GLOBAL";
public static final String SYSTEM_PROPERTY = "spring.security.strategy";
private static String strategyName = System.getProperty("spring.security.strategy");
private static SecurityContextHolderStrategy strategy;
private static int initializeCount = 0;
public SecurityContextHolder() {
}
public static void clearContext() {
strategy.clearContext();
}
public static SecurityContext getContext() {
return strategy.getContext();
}
public static int getInitializeCount() {
return initializeCount;
}
private static void initialize() {
if (!StringUtils.hasText(strategyName)) {
strategyName = "MODE_THREADLOCAL";
}
if (strategyName.equals("MODE_THREADLOCAL")) {
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 void setContext(SecurityContext context) {
strategy.setContext(context);
}
public static void setStrategyName(String strategyName) {
SecurityContextHolder.strategyName = strategyName;
initialize();
}
public static SecurityContextHolderStrategy getContextHolderStrategy() {
return strategy;
}
public static SecurityContext createEmptyContext() {
return strategy.createEmptyContext();
}
public String toString() {
return "SecurityContextHolder[strategy='" + strategyName + "'; initializeCount=" + initializeCount + "]";
}
static {
initialize();
}
}
複製代碼
SecurityContextHolder 定義三個靜態常量描述三種不一樣存儲策略,在靜態代碼塊中初始化,根據不一樣的strategyName初始化不一樣的存儲策略,能夠調用配置系統變量或者調用setStrategyName改變策略。post
在默認狀況下,從子線程中獲取登陸數據是獲取不到的,由於默認是MODE_THREADLOCAL策略。this
存儲策略在System.getProperty("spring.security.strategy");加載,能夠配置系統變量來修改策略。
在VM option參數添加
-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL
SpringContextHolder默認將用戶信息存儲在ThreadLocal中,在SpringBoot中不一樣的請求使用不一樣的線程處理,是怎麼獲取到用戶的信息的呢? 下篇文章揭曉,有懂的能夠在評論區留言。