【问题标题】:Thread.CurrentPrincipal Identity.Name is null in asp.net mvc 6 (vNext)Thread.CurrentPrincipal Identity.Name 在 asp.net mvc 6 (vNext) 中为空
【发布时间】:2015-04-16 14:39:18
【问题描述】:

我正在使用 Asp.Net MVC6 (vNext)、Identity 3.0.* beta3 开发一个 Web 应用程序。这是具有 UI、服务和数据库的 3 层应用程序。我可以使用SignInManager 类的 Asp.net 身份登录用户,并且我正在尝试使用服务层中的角色和声明检索用户详细信息,以便我可以验证用户是否有权执行某些操作。

我们如何从服务层的当前请求主体身份获取当前用户名。在 MVC 5 中,我使用了Thread.CurrentPrincipal 来执行此操作。但是在 MVC6 中,Identity.NameThread.CurrentPrincipal 设置为 null。

有人可以告诉我该怎么做吗?另外,如果您有更好的解决方案,请告诉我。

【问题讨论】:

  • 我知道 HttpApplication 对象在获取线程时将线程的主体设置为 HttpContext 的。你试过HttpContext.Current.User,也为空吗?
  • @RazvanDumitru 感谢您的回复。能否告诉我如何在 Service 层获取 HttpContext ?
  • ASP.Net 5 以完全可单元测试的方式编写,尽可能少的静态属性。因此,诸如用户主体之类的值应作为参数传递到您的服务层。 (这并不是真正的答案,而是建议遵循公认的设计模式。)此外,async 正在改变事物的方式,线程特定的信息可能会丢失。
  • @MattDeKrey 是对的。您应该始终将您的上下文数据和用户信息数据(尤其是)以及您需要的所有值作为参数传递给服务层。

标签: c# asp.net-mvc asp.net-core asp.net-core-mvc


【解决方案1】:

如果您在 Startup.cs 或配置依赖注入的任何位置调用了 AddIdentity(),它将自动注册一个单例 HttpContextAccessor 以满足 IHttpContextAccessor 服务。

此时,在您的服务层中,您可以注入IHttpContextAccessor 并从那里检索上下文。这假设您了解依赖注入系统的工作原理并使用它来实例化您的服务层类,而不仅仅是newing 它们。

如果您希望能够方便地访问 ClaimsPrincipal 中暴露在 HttpContext 中的 IdName,请确保导入 System.Security.Claims,以便您可以访问提供GetUserName()GetUserId() 扩展方法。

(这是与 VS2015 RC1 一起发布的 beta4 软件包的最新版本)

【讨论】:

  • 我什至建议更进一步,并完全删除任何与您的代码相关的HttpContext 的提及,因为您真正想要的只是一项能够为您提供当前ClaimsPrincipal 的服务。创建一个公开CurrentPrincipal 属性的ICurrentClaimsPrincipal 接口。创建一个实现它的类,该类使用IHttpContextAccessor 依赖项读取当前主体并从CurrentPrincipal 属性返回它。
【解决方案2】:

假设您可以在您的层中传递访问服务,则 HttpContext.Current 的等价物是 IHttpContextAccessor.HttpContext,它是托管层公开的服务。

【讨论】:

    【解决方案3】:

    可能有更好的方法。但是现在我正在添加一个自定义中间件,如下面的代码所示,以便在 startup.cs 类的当前线程中注入用户名。在服务层,我将使用 Thread.GetData(Thread.GetNamedDataSlot("current-username")) 读取它。

    public class Startup {
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory)
        {
    
            //.... code
    
            app.UseIdentity();
    
            //... more code
    
            app.Use(async (ctx, next) =>
            {
                var httpContext = (ctx as HttpContext);
                if (httpContext.User.Identity.IsAuthenticated)
                {
                    Thread.SetData(Thread.GetNamedDataSlot("current-username"), httpContext.User.Identity.Name);
                }
                await next();
            });
    
            //... more code
    
            app.UseMvc(routes =>
                {
                    routes.MapRoute(
                        name: "default",
                        template: "{controller}/{action}/{id?}",
                        defaults: new { controller = "Home", action = "Index" });
                });
    
        }
    }
    

    【讨论】:

    • 编写自己的安全相关代码充满危险。例如。在这里,如果经过身份验证的用户发出请求,他们最终会进入线程槽。如果未经身份验证的用户随后发出请求并且它发生在同一个线程上,则您不会清除用户名,因此他们仍将作为第一个用户进行身份验证==您刚刚打开自己被黑客攻击。 if (!httpContext.User.Identity.IsAuthenticated) then....清除线程认证槽
    • 谢谢乔恩。在服务层中读取它之后,我实际上正在清除插槽。每个请求我只需要一次用户名。但是你提出了很好的观点。我希望有更好的方法来做到这一点。
    • 另外,直接使用这样的线程静态数据,一旦开始使用异步方法,就会导致悲伤。您现在还通过让服务层直接访问线程本地数据而不是声明对可以模拟的 IHttpContextAccessor 的依赖关系,使服务层更难测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-06
    • 1970-01-01
    相关资源
    最近更新 更多