首页 » java

spring boot Security 获取上下文用户信息的问题总结

   发表于:java评论 (0)   热度:5
@RequestMapping("/hello")
    public String hello() {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println( "用户名:" + authentication.getName() );

        new Thread(() -> {
            // 先复制父线程上下文
            SecurityContextHolder.setContext(context);
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            System.out.println( "子线程用户名2:" + auth );
        }).start();
       
        return "Hello World!";
    }

如何把 获取用户信息做成一个公共的方法使其 可以在子线程 能方便的获取 登录用户信息

现全局可用的安全上下文传递,使其在子线程中也能像主线程一样访问,可以通过以下几种方式实现。我将从简单到复杂给出解决方案

方案1:静态工具类(最简单)

// 创建安全上下文工具类
public class SecurityContextUtils {
    private static final InheritableThreadLocal<SecurityContext> contextHolder = 
        new InheritableThreadLocal<>();
    
    public static void setContext(SecurityContext context) {
        contextHolder.set(context);
    }
    
    public static SecurityContext getContext() {
        return contextHolder.get();
    }
    
    public static void clear() {
        contextHolder.remove();
    }
}

// 使用方式(在需要传递上下文的地方):
SecurityContext context = SecurityContextHolder.getContext();
SecurityContextUtils.setContext(context);

new Thread(() -> {
    SecurityContext ctx = SecurityContextUtils.getContext();
    Authentication auth = ctx.getAuthentication();
    System.out.println("子线程用户: " + auth.getName());
}).start();

方案2:Spring Bean + AOP(推荐生产环境使用)

@Component
public class SecurityContextPropagator {
    public void executeInContext(Runnable task) {
        SecurityContext context = SecurityContextHolder.getContext();
        new Thread(() -> {
            try {
                SecurityContextHolder.setContext(context);
                task.run();
            } finally {
                SecurityContextHolder.clearContext();
            }
        }).start();
    }
}

// 使用方式:
@Autowired
private SecurityContextPropagator propagator;

propagator.executeInContext(() -> {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    System.out.println("子线程用户: " + auth.getName());
});

方案3:自定义线程池(最完整方案)

@Configuration
public class ThreadPoolConfig {
    
    @Bean
    public ExecutorService securityContextAwareExecutor() {
        return new ContextCopyingExecutorService(
            Executors.newCachedThreadPool()
        );
    }
    
    static class ContextCopyingExecutorService extends ThreadPoolExecutor {
        // 实现所有构造方法...
        
        @Override
        public void execute(Runnable command) {
            SecurityContext context = SecurityContextHolder.getContext();
            super.execute(() -> {
                try {
                    SecurityContextHolder.setContext(context);
                    command.run();
                } finally {
                    SecurityContextHolder.clearContext();
                }
            });
        }
    }
}

// 使用方式:
@Autowired 
private ExecutorService securityContextAwareExecutor;

securityContextAwareExecutor.execute(() -> {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    System.out.println("线程池用户: " + auth.getName());
});

方案4:Spring Security 5.3+ 的官方方案

// 配置类
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityContextHolderStrategy securityContextHolderStrategy() {
        return new InheritableThreadLocalSecurityContextHolderStrategy();
    }
}

// 使用方式(无需修改原有代码):
new Thread(() -> {
    // 现在能自动获取父线程上下文
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
}).start();

 

方案 优点 缺点 适用场景
静态工具类 简单直接 需要手动调用 小型应用
Spring Bean 可注入管理 需要依赖Spring 常规Spring项目
自定义线程池 完整控制 实现复杂 需要线程池的场景
Spring官方方案 最规范 需要5.3+版本 新项目

 

 

 

 

如果是新项目:直接使用方案4(Spring Security 5.3+)
传统Spring项目:选择方案2(Spring Bean)
需要精细控制线程:用方案3(自定义线程池)
快速原型开发:用方案1(静态工具类)

 

(。・v・。)
喜欢这篇文章吗?欢迎分享到你的微博、QQ群,并关注我们的微博,谢谢支持。