JwtBearerAuthentication不会设置HttpContext用户

问题描述

我很难找到有关此主题的相关信息:.NetCore Web API中的JwtBearerAuthentication。令牌是由IdentityServer发行并通过前端客户端应用程序发送的OIDC id令牌。令牌已正确接收和验证,从而产生了经过身份验证的ClaimsPrincipal(具有15个声明),但是当请求到达应用程序时,HttpContext中不存在该主体。有一个未经身份验证的匿名用户,而不是验证令牌时出现的匿名用户。

此api由其作者(不是我)构想为使用OIDC cookie身份验证,但我正在尝试将其转换为使用JWT承载身份验证。

据我所知,我根据发现的所有示例正确地完成了所有工作,并且没有想法。任何帮助表示赞赏。

我已经在下面发布了整个Startup.cs文件,对于所有代码,我们感到抱歉,但是您应该能够看到所有相关内容...

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Autofac;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Autofac.Configuration;
using Autofac.Extensions.DependencyInjection;
using CAS.Authorization.Api.Configuration;
using CAS.Authorization.Api.Defaults.Configuration;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Swashbuckle.AspNetCore.Swagger;

namespace CAS.Authorization.Api
{
    [ExcludeFromCodeCoverage]
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddAuthentication(options =>
                {
                    //options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    //options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                   // options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                //.AddCookie()
                .AddJwtBearer(options =>
                {
                    options.Authority = "http://localhost:40800";
                    options.RequireHttpsMetadata = false;
                    options.SaveToken = true;
                    options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                    {
                        ValidateIssuer = false,ValidIssuer = "http://localhost:40800",ValidateAudience = false,ValidAudience = "http://localhost:22426"
                    };
                    options.Events = new JwtBearerEvents()
                    {
                        OnTokenValidated = async context =>
                        {
                            //This does not work,however the context.Principal IS authenticated
                            //and DOES have claims (15 of them).
                            context.HttpContext.User = context.Principal;
                        }
                    };
                });
            //.AddOpenIdConnect(options =>
            //{
            //    options.SignInScheme = "Cookies";
            //    options.Authority = "http://localhost:40800";
            //    options.ClientId = "defaultClientId";
            //    options.SignInScheme = "Cookies";
            //    options.RequireHttpsMetadata = false;
            //    options.ResponseType = "code";
            //    options.Scope.Add("profile");
            //    options.GetClaimsFromUserInfoEndpoint = true;
            //    options.SaveTokens = true;
            //    options.ClaimActions.MapAllExcept("iss","nbf","exp","aud","nonce","iat","c_hash");
            //});

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            services.AddHttpContextAccessor();
            services.AddAuthorization(PolicyConfiguration.SetupPolicies);

            services.AddSingleton<IAuthorizationHandler,AccessPolicyHandler>();
            services.AddTransient<IActionContextAccessor,ActionContextAccessor>();

            //Api Versioning
            //services.AddApiVersioning(o =>
            //{
            //    o.ReportApiVersions = true;
            //    o.ApiVersionReader = new UrlSegmentApiVersionReader();
            //});

            services.Configure<BusSettings>(Configuration.GetSection("BusSettings"));

            //Swagger
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1",new Info { Title = "CAS Authorization API",Version = "v1" });
                
                //Locate the XML file being generated by ASP.NET...
                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.XML";
                var xmlPath = Path.Combine(AppContext.BaseDirectory,xmlFile);

                //... and tell Swagger to use those XML comments.
                c.IncludeXmlComments(xmlPath);
            });

            // Add Autofac
            // ConfigurationModule accepts JSON configuration for other modules/components to register,// instead of having references here to those modules,directly.
            // If we ever want to remove those direct references,this should still work.
            // config.AddJsonFile comes from Microsoft.Extensions.Configuration.Json
            var config = new ConfigurationBuilder();
            config.AddJsonFile("modules.json");

            // Register the ConfigurationModule with Autofac.
            var module = new ConfigurationModule(config.Build());
            var builder = new ContainerBuilder();
            builder.RegisterAssemblyModules();
            builder.RegisterModule(module);
            builder.Populate(services);

            var container = builder.Build();
            return new AutofacServiceProvider(container);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app,IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
                
            }

            app.UseCookiePolicy();
            app.UseAuthentication();

            app.UseHttpsRedirection();
            app.UseMvc();

            //Swagger UI
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json","CAS Authorization API - v1");
                c.RoutePrefix = string.Empty;
            });
        }
    }
}

在以下方法GetClaimsByOrganization中添加了尝试访问用户的示例:

    public class UserIdentityService : IUserIdentityService
    {
        private HttpContext _context;

        public UserIdentityService(IHttpContextAccessor contextAccessor)
        {
            _context = contextAccessor?.HttpContext ?? throw new ArgumentNullException();
        }

        public string UserName => _context.User.FindFirst(ClaimTypes.Name)?.Value;

        public Guid Sub => Guid.Parse(_context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value);

        public Claim[] GetPermissionClaims(string permissionName,string resourceName)
        {
            if (string.IsNullOrWhiteSpace(permissionName)) throw new ArgumentException("Invalid permission name");

            if (string.IsNullOrWhiteSpace(resourceName)) throw new ArgumentException("Invalid resource name");

            return _context.User?.Claims?.Where(c => c.Type.Contains("ORG:") && c.Value == resourceName.ToUpper() + permissionName.ToUpper()).ToArray();
        }

        public Claim[] GetClaimsByOrganization(string orgName)
        {
            if (string.IsNullOrWhiteSpace(orgName)) throw new ArgumentException("Invalid organization name");

            return _context.User?.Claims?.Where(c => c.Type == "ORG:" + orgName.ToUpper()).ToArray();
        }
    }

解决方法

问题已解决。这是Identity Server,客户端应用程序和API的概念证明,它们都在Visual Studio Debug中运行,并且具有尽可能多的默认设置。我当时用http打API。当我在它的https端口上点击它时,它起作用了。显然,身份验证中间件仅在请求使用https时设置用户。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...