You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OnActionExecuting是否线程安全?控制器内赋值userId的实现是否正确

结论

你提供的实现不具备线程安全性,且存在非常高的业务风险。

问题原因

你的代码中将userId、path作为控制器的类成员变量存储,存在两个核心问题:

  • 默认情况下ASP.NET Core的控制器是瞬态生命周期,每次请求会创建新的控制器实例,单请求场景下看似可以正常运行,但该实现强依赖控制器的生命周期规则,可维护性极差。如果后续你调整了控制器的注册生命周期为单例/作用域,或者开启了控制器复用配置,多个请求会共用同一个控制器实例,并发请求执行OnActionExecuting时会互相覆盖成员变量的值,直接出现用户身份串用的严重安全漏洞。
  • 就算使用默认瞬态控制器,如果你在异步Action中跨多个异步上下文调用这两个变量,也可能因为上下文切换出现取值异常。

可行替代方案

方案1:封装为控制器只读属性(单控制器场景用)

不需要提前赋值存储,每次使用时实时从请求上下文取值,完全规避线程安全问题:

public class AccountController : Controller
{
    // 用到时实时计算取值
    protected string UserId => User.FindFirstValue(ClaimTypes.NameIdentifier);
    protected string RequestPath => $"{HttpContext.Request.Path.Value}[{HttpContext.Request.Method}]";
}

方案2:全局过滤器存HttpContext.Items(多控制器共用场景用)

如果需要在项目所有控制器中都用到这两个值,可以用全局过滤器实现,把值存在单次请求唯一的HttpContext.Items集合中:

  1. 定义过滤器:
public class UserInfoCaptureFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var userId = context.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
        var path = $"{context.HttpContext.Request.Path.Value}[{context.HttpContext.Request.Method}]";
        // HttpContext.Items是单次请求内的共享字典,不会跨请求串值
        context.HttpContext.Items["_CurrentUserId"] = userId;
        context.HttpContext.Items["_CurrentRequestPath"] = path;
        await next();
    }
}
  1. 注册全局过滤器:
// Program.cs中添加配置
builder.Services.AddControllers(options =>
{
    options.Filters.Add<UserInfoCaptureFilter>();
});
  1. 使用时直接从上下文取即可:
var userId = HttpContext.Items["_CurrentUserId"] as string;

方案3:身份校验用官方授权机制

如果你的核心需求是用户身份校验,优先用ASP.NET Core自带的认证授权中间件、策略授权功能,框架会自动校验Claims合法性,不需要手动存储userId做校验。

内容的提问来源于stack exchange,提问作者Анатолій Тимошенко

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 12:27:04