Spring Security异步无法获取用户认证信息的解决方法
作者:忆昔年.
原因:
Spring Security中的上下文SecurityContext的管理策略有三种
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"; }
默认的管理策略是:MODE_THREADLOCAL,对应的策略类是
org.springframework.security.core.context.ThreadLocalSecurityContextHolderStrategy
以下是 Spring Security 初始化策略的代码:
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"; // 获取系统配置的策略 private static String strategyName = System.getProperty(SYSTEM_PROPERTY); // 预初始化策略 private static SecurityContextHolderStrategy strategy; private static void initializeStrategy() { if (MODE_PRE_INITIALIZED.equals(strategyName)) { Assert.state(strategy != null, "When using " + MODE_PRE_INITIALIZED + ", setContextHolderStrategy must be called with the fully constructed strategy"); return; } if (!StringUtils.hasText(strategyName)) { // Set default strategyName = MODE_THREADLOCAL; } if (strategyName.equals(MODE_THREADLOCAL)) { strategy = new ThreadLocalSecurityContextHolderStrategy(); return; } if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) { strategy = new InheritableThreadLocalSecurityContextHolderStrategy(); return; } if (strategyName.equals(MODE_GLOBAL)) { strategy = new GlobalSecurityContextHolderStrategy(); return; } // 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); } } }
ThreadLocalSecurityContextHolderStrategy 里保存Context的方式是 ThreadLocal,ThreadLocal 子线程并不能获取父类线程的 ThreadLocalMap,
所以就导致了异步无法获取到用户认证信息。
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy { private static final ThreadLocal<Supplier<SecurityContext>> contextHolder = new ThreadLocal<>(); }
解决方法:
更改上下文的管理策略 SecurityContextHolderStrategy 为 MODE_INHERITABLETHREADLOCAL,即
InheritableThreadLocalSecurityContextHolderStrategy,里面保存上下文的方式是 InheritableThreadLocal,可以获取父线程的 ThreadLocalMap。
final class InheritableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy { private static final ThreadLocal<Supplier<SecurityContext>> contextHolder = new InheritableThreadLocal<>(); }
InheritableThreadLocal可以获取到子线程信息,主要是因为它是ThreadLocal的一个子类,设计用来在父子线程间传递数据。
ThreadLocal变量通常只在当前线程中存在,并且每个线程都有其自己的ThreadLocalMap,
这样保证了线程间的数据隔离。然而,在某些情况下,我们需要父线程中的数据能够在由父线程创建的子线程中被访问,
这时就需要使用InheritableThreadLocal。
InheritableThreadLocal的实现机制使其能够在子线程中访问父线程的数据。具体来说,当父线程创建一个子线程时,
InheritableThreadLocal会采取措施确保子线程可以访问到父线程中设置的InheritableThreadLocal变量。
这通常涉及到在子线程的Thread对象中复制父线程的ThreadLocalMap,从而使子线程能够访问到父线程中设置的变量。
这种机制允许在多线程环境中,特别是在使用线程池等场景下,实现父子线程间数据的传递和共享。
需要注意的是,虽然InheritableThreadLocal提供了在子线程中访问父线程数据的能力,但它并不适用于所有情况,
特别是在复杂的线程交互中可能需要更精细的控制。此外,使用InheritableThreadLocal时也需要注意避免内存泄漏等问题,因为如果不当使用,
它可能会导致资源的不当占用。
代码:
- 1、可以在配置 Security 的配置类中添加 Bean
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public MethodInvokingFactoryBean methodInvokingFactoryBean() { MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean(); factoryBean.setTargetClass(SecurityContextHolder.class); factoryBean.setTargetMethod("setStrategyName"); factoryBean.setArguments(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); return factoryBean; } }
- 2、可以在配置 Security 的配置类中添加类初始化方式,会在注入 SecurityContextHolder 之后调用其中静态方法更改 SecurityContextHolderStrategy 的实现类
@Configuration @EnableWebSecurity public class SecurityConfig { @PostContruct public void init() { SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); } }
- 3、可以在启动项加上 -Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL 参数,启动时更改 SecurityContextHolderStrategy 的实现类
以上就是Spring Security异步无法获取用户认证信息的解决方法的详细内容,更多关于Spring Security异步无法获取信息的资料请关注脚本之家其它相关文章!