IHttpContextAccessor.HttpContext.User.Identity 显示 CurrentUserService 服务中的所有空属性

c0y*_*teX 4 c# identity dependency-injection httpcontext clean-architecture

我正在尝试使用Jason Taylor 的 Clean Architecture Template,这个模板使用 NSwag 自动创建一个 TypeScript 客户端(Angular),但我不需要创建一个 TS 客户端,所以我的主要目标是用 Razor Pages 替换它。我已经能够做到这一点,但当CurrentUserService被实例化时,我遇到了问题,它应该在这一行中设置 UserId:

UserId = httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
Run Code Online (Sandbox Code Playgroud)

这是完整的CurrentUserService

using CleanREC0.Application.Common.Interfaces;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;

namespace CleanREC0.WebUI.Services
{
    public class CurrentUserService : ICurrentUserService
    {
        public CurrentUserService(IHttpContextAccessor httpContextAccessor)
        {
            UserId = httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
        }

        public string UserId { get; }
    }
}
Run Code Online (Sandbox Code Playgroud)

第一次甚至 HttpContext 为,这很好,因为一切都在初始化,但随后的调用总是在所有httpContextAccessor.HttpContext.User.Identity属性上返回null,用户显示了一个身份,但它的所有属性和声明都是nullYield no results,即使用户正确登录。

这是我对模板所做的事情的列表:

  • 摆脱了NSwag和所有与TypeScript相关的东西
  • 摆脱了IdentityServer
  • 替换了IdentityDbContext 的ApiAuthorizationDbContext

这是我的 StartUp 课程:

public class Startup
    {
        public Startup(IConfiguration configuration, IWebHostEnvironment environment)
        {
            Configuration = configuration;
            Environment = environment;
        }

        public IConfiguration Configuration { get; }
        public IWebHostEnvironment Environment { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddApplication();
            services.AddInfrastructure(Configuration, Environment);    

            services.AddScoped<ICurrentUserService, CurrentUserService>();

            services.AddHttpContextAccessor();
            services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); //I have added this line on as suggested on other post, but made no difference.


            services.AddHealthChecks()
                .AddDbContextCheck<ApplicationDbContext>();

            services.AddRazorPages()
                .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<IApplicationDbContext>());
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseCustomExceptionHandler();
            app.UseHealthChecks("/health");
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
            });
        }
    }
Run Code Online (Sandbox Code Playgroud)

这是从ConfigureServices调用的AddApplication

public static IServiceCollection AddApplication(this IServiceCollection services)
        {
            services.AddAutoMapper(Assembly.GetExecutingAssembly());
            services.AddMediatR(Assembly.GetExecutingAssembly());
            services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPerformanceBehaviour<,>));
            services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestValidationBehavior<,>));

            return services;
        }
Run Code Online (Sandbox Code Playgroud)

以及也在那里被调用的AddInfrastructure

public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration, IWebHostEnvironment environment)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    configuration.GetConnectionString("DefaultConnection"), 
                    b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));

            services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());

            services.AddDefaultIdentity<ApplicationUser>()
                .AddEntityFrameworkStores<ApplicationDbContext>();

            if (environment.IsEnvironment("Test"))
            {

            }
            else
            {
                services.AddTransient<IDateTime, DateTimeService>();
                services.AddTransient<IIdentityService, IdentityService>();
                services.AddTransient<ICsvFileBuilder, CsvFileBuilder>();
            }

            services.AddAuthentication();

            return services;
        }
Run Code Online (Sandbox Code Playgroud)

其他一切似乎都运行良好。在Razor 页面级别,用户变量及其身份包含所有预期信息和声明。

我确定我遗漏了一些东西,有人可以指出我正确的方向吗?

c0y*_*teX 6

LinkedListT 在评论中的链接让我找到了正确的方向。

该链接指向一个解释这一点的答案:

在 ASP.NET MVC 框架下,当控制器类按照您的预期构建时,HttpContext(以及因此 HttpContext.Session)并未设置,但它稍后由 ControllerBuilder 类设置(“注入”)。

我正在使用的模板附带的CurrentUserService类尝试读取构造函数中的用户声明,因此我将代码移动到属性的 getter,它会在实际使用该属性时执行,然后所有声明和属性都是人口稠密。

我的新CurrentUserService如下所示:

using CleanREC0.Application.Common.Interfaces;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;

namespace CleanREC0.WebUI.Services
{
    public class CurrentUserService : ICurrentUserService
    {
        private IHttpContextAccessor _httpContextAccessor;

        public CurrentUserService(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public string UserId { get { return _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier); }}
    }
}
Run Code Online (Sandbox Code Playgroud)