【问题标题】:JwtBearerAuthentication does not set the HttpContext UserJwtBearerAuthentication 没有设置 HttpContext 用户
【发布时间】:2020-12-30 07:48:03
【问题描述】:

我很难找到有关此主题的相关信息:.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();
        }
    }

【问题讨论】:

  • 请显示您尝试访问用户对象的位置
  • 谢谢,我编辑了帖子以添加尝试访问用户的示例。
  • 检查这两个链接 startup.cscontroller with authorize attribute ,我认为您不必在 Startup 类中设置 User ,.Net核心应该这样做,但是您的 UserIdentityService 必须从控制器调用使用 Authorize 属性,这可能会有所帮助
  • 感谢您查看并分享这些示例。我知道不必在启动中设置用户,但这样做确实允许我打破它并看到 context.Principal 确实具有正确的值。这表明令牌已被接收和验证,并且所有预期的声明都存在。但是稍后访问时,该主体不是 HttpContext 中的主体。 Authorize 属性在所涉及的控制器上,这没有区别。

标签: c# asp.net-core authentication bearer-token jwt-auth


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-06
    • 1970-01-01
    • 1970-01-01
    • 2011-01-07
    • 1970-01-01
    • 2022-01-09
    • 2021-02-07
    • 1970-01-01
    相关资源
    最近更新 更多