Spring Boot2.x教程:(十三)Spring Security新线程为什么获取不到用户信息

Spring Security新线程为什么获取不到用户信息

大家好,我是欧阳方超,微信公众号同名。在这里插入图片描述

1 概述

周五了,正想着如何过周末呢,前端那边报出来一个问题,之前正常使用的一个查询接口,现在不能使用了,打开后台日志,里面赫然显示获取当前用户id失败,到底是怎么回事呢,下面就详细说说。

2 事情经过

这个接口之前确实能用,这个接口涉及到的Service层的方法暂且称为方法A吧,方法A间接调用了方法C,而方法C中有获取当前用户id的操作,之前方法C被注释掉了,今天启用了,而方法A又是启用了多线程的方式间接调用了方法C,这是今天所做的改动。
假设下面的方法为方法A:

import java.util.concurrent.CompletableFuture;

public class YourService {

    public void yourMethod() {
        CompletableFuture.runAsync(() -> {
            
            // 执行其他业务逻辑
        });
    }
}

下面的方法为方法C:

private void saveApiInfo(String url, String params, HttpResponseInfo info) {
        Long userId = SecurityUtils.getUserId();
    }

3 原因分析

为什么改动会带来这个问题呢,原因就出在方法A中以多线程的形式间接调用了方法C,造成了异步执行的上下文中没有正确传递Spring Security的安全上下文(SecurityContext )。SecurityContextHolder默认使用ThreadLocal来存储认证信息(本项目把用户id放到了认证信息里),即SecurityContext,在新的线程中使用SecurityContextHolder获取SecurityContext时,获取到的是新线程中SecurityContext,无法获取原始线程的SecurityContext,因此也就无法获取用户id。

4 解决方案

4.1 手动传递SecurityContext

可以在提交CompletableFuture任务之前,获取当前线程的SecurityContext,然后在异步任务重显式地设置它。
首先,获取SecurityContextHolder中的SecurityContext:

SecurityContext currentContext = SecurityContextHolder.getContext();

然后在CompletableFuture的任务中设置它:

CompletableFuture.runAsync(() -> {
    SecurityContextHolder.setContext(currentContext);
    // 在这里执行需要安全上下文的操作
});

5 总结

为了确保在多线程环境中能够正确获取到用户 ID
需要保证新启用的线程拥有当前线程的SecurityContext,本文介绍的手动传递SecurityContext可以做到这一点。通过这种方法,可以更有效地管理异步操作,同时保持安全上下文的一致性。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值