IHttpContextAccessor.HttpContext.User.Identity shows all null properties in CurrentUserService service

我的梦境 提交于 2021-02-15 04:54:09

问题


I am trying to use Jason Taylor's Clean Architecture Template, this template uses NSwag to automatically create a TypeScript Client (Angular), but I don't need to create a TS client, so my main goal is to replace it with Razor Pages. I've been able to achieve this to a high grade, but I'm having trouble with the CurrentUserService whenever it gets instantiated it is supposed to set the UserId in this line:

UserId = httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);

This is the full 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; }
    }
}

The first time even the HttpContext is null and that's fine, since everything is initializing, but the subsequent calls always return null on all of httpContextAccessor.HttpContext.User.Identity properties, The User Shows an Identity alright but all of it's properties and claims are null or Yield no results, even with a user properly logged on.

Here is a list of things I've done to the template:

  • Got rid of NSwag and everything TypeScript Related
  • Got rid of IdentityServer
  • Replaced ApiAuthorizationDbContext for IdentityDbContext

This is my StartUp class:

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();
            });
        }
    }

This is the AddApplication that gets called from ConfigureServices:

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;
        }

And the AddInfrastructure that also gets called there:

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;
        }

Everything else seems to be working fine. At Razor Page level the User variable and it's Identity contain all expected information and claims.

I'm sure I'm missing something, can someone please point me in the right direction?


回答1:


LinkedListT's link in the comments got me in the right direction.

The link points to an answer which explains this:

under the ASP.NET MVC framework, the HttpContext (and therefore HttpContext.Session) is not set when the controller class is contructed as you might expect, but it set ("injected") later by the ControllerBuilder class.

The CurrentUserService class that comes with the template I'm using tries to read the user claims in the constructor, so I moved the code to the property's getter, which executes until latter when the property is actually used, by then all claims and properties are well populated.

My new CurrentUserService looks like this:

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); }}
    }
}


来源:https://stackoverflow.com/questions/59793111/ihttpcontextaccessor-httpcontext-user-identity-shows-all-null-properties-in-curr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!