Asp.net core 2.0 Jwt 不会忽略 AllowAnonymous

Posted

技术标签:

【中文标题】Asp.net core 2.0 Jwt 不会忽略 AllowAnonymous【英文标题】:Asp.net core 2.0 Jwt doesn't ignore AllowAnonymous 【发布时间】:2018-05-19 23:02:28 【问题描述】:

我正在使用 .net core 2.0 和 jwt 不记名身份验证 (https://github.com/aspnet/Security) 制作一个小型新项目

这是我的 Startup.cs

        /// <summary>
        ///     This method gets called by the runtime. Use this method to add services to the container.
        /// </summary>
        /// <param name="services"></param>
        public void ConfigureServices(IServiceCollection services)
        
            // Add entity framework to services collection.
            var sqlConnection = Configuration.GetConnectionString("SqlServerConnectionString");
            services.AddDbContext<RelationalDatabaseContext>(
                options => options.UseSqlServer(sqlConnection, b => b.MigrationsAssembly(nameof(Main))));

            // Injections configuration.
            services.AddScoped<IUnitOfWork, UnitOfWork>();
            services.AddScoped<DbContext, RelationalDatabaseContext>();
            services.AddScoped<IEncryptionService, EncryptionService>();
            services.AddScoped<IIdentityService, IdentityService>();
            services.AddScoped<ITimeService, TimeService>();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            // Requirement handler.
            services.AddScoped<IAuthorizationHandler, SolidAccountRequirementHandler>();
            services.AddScoped<IAuthorizationHandler, RoleRequirementHandler>();

            // Load jwt configuration from setting files.
            services.Configure<JwtConfiguration>(Configuration.GetSection(nameof(JwtConfiguration)));
            services.Configure<ApplicationSetting>(Configuration.GetSection(nameof(ApplicationSetting)));

            // Build a service provider.
            var serviceProvider = services.BuildServiceProvider();
            var jwtBearerSettings = serviceProvider.GetService<IOptions<JwtConfiguration>>().Value;

            // Cors configuration.
            var corsBuilder = new CorsPolicyBuilder();
            corsBuilder.AllowAnyHeader();
            corsBuilder.AllowAnyMethod();
            corsBuilder.AllowAnyOrigin();
            corsBuilder.AllowCredentials();

            // Add cors configuration to service configuration.
            services.AddCors(options =>  options.AddPolicy("AllowAll", corsBuilder.Build()); );
            services.AddOptions();

            // This can be removed after https://github.com/aspnet/IISIntegration/issues/371
            var authenticationBuilder = services.AddAuthentication(options =>
            
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            );

            authenticationBuilder.AddJwtBearer(o =>
            
                // You also need to update /wwwroot/app/scripts/app.js
                o.Authority = jwtBearerSettings.Authority;
                o.Audience = jwtBearerSettings.Audience;
                o.RequireHttpsMetadata = false;

                o.SecurityTokenValidators.Clear();
                o.SecurityTokenValidators.Add(new JwtBearerValidator());

                o.Events = new JwtBearerEvents()
                
                    OnAuthenticationFailed = c =>
                    
                        c.NoResult();

                        c.Response.StatusCode = 500;
                        c.Response.ContentType = "text/plain";
                        if ("dev".Equals(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")))
                        
                            // Debug only, in production do not share exceptions with the remote host.
                            return c.Response.WriteAsync(c.Exception.ToString());
                        
                        return c.Response.WriteAsync("An error occurred processing your authentication.");
                    
                ;
            );

            #region Mvc builder

            // Construct mvc options.
            var mvcBuilder =
                services.AddMvc(mvcOptions =>
                
                    //only allow authenticated users
                    var policy = new AuthorizationPolicyBuilder()
                        .RequireAuthenticatedUser()
                        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
                        .AddRequirements(new SolidAccountRequirement())
                        .Build();

                    mvcOptions.Filters.Add(new AuthorizeFilter(policy));
                );

            // Add json configuration/
            mvcBuilder.AddJsonOptions(options =>
            
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            );

            #endregion
        

        /// <summary>
        ///     This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        /// </summary>
        /// <param name="app"></param>
        /// <param name="env"></param>
        /// <param name="loggerFactory"></param>
        /// <param name="serviceProvider"></param>
        public void Configure(IApplicationBuilder app,
            IHostingEnvironment env,
            ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
        
            // Enable logging.
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            // Use JWT Bearer authentication in the system.
            app.UseAuthentication();

            // Enable cors.
            app.UseCors("AllowAll");

            // Enable MVC features.
            app.UseMvc();
        

通过这些配置,jwt 已在我的 Web 应用程序中启用。但是,我目前面临一件事:

使用 API 不需要身份验证(放置在 [AllowAnonymous] 属性下),如果我在请求中传递 Authorization 标头,OnAuthenticationFailed将引发事件(由于未检测到令牌)。

我的问题是:如何让我的 jwt 身份验证自动忽略标记为 AllowAnonymous 的方法或控制器

谢谢,

【问题讨论】:

【参考方案1】:

不要使用 OnAuthenticationFailed,而是尝试将其放入 OnChallenge

o.Events = new JwtBearerEvents()

    OnChallenge = c =>
    
        c.HandleResponse();
        c.Response.StatusCode = 500;
        c.Response.ContentType = "text/plain";
        return c.Response.WriteAsync("An error occurred processing your authentication.");
    
;

【讨论】:

此事件在这种特殊情况下不会触发。 我的情况与这篇文章的原始问题完全相同。我花了几天时间才找到这个解决方案,它非常适合我的两个项目。在什么特殊情况下它不起作用?请说明或给出您的解决方案,而不是投反对票。我只是想帮忙。这是我在 *** 上的第一个答案,现在你让我气馁了。 ManWaiLorentzYip,您不应该因互联网投票而气馁。如果你让它来找你,你会气馁很多。继续提供帮助并提供信息,我们都会从中学习。我在这个线程中有同样的问题,你的建议似乎有效,但我不知道为什么。我不知道为什么将代码放在 OnChallenge 与 OnAuthenticationFailed 中会产生如此不同,因为我所做的只是捕获身份验证失败并将其映射到我自己的响应中。文档没有帮助,您知道这些事件实际上是做什么的吗?谢谢 @BayoO'liliJason',非常感谢您的鼓励。实际上,让我失望的是有人没有尝试就对我的解决方案投了反对票。无论如何,我还发现 asp.net 核心文档不是很有帮助。然后在this的The JwtBearerHandler HandleUnauthorisedAsync方法部分找到了一些线索, 和source code 的第 215 行。所以 HandleChallengeAsync 似乎是身份验证失败时设置状态码 401 的地方。这就是我尝试 OnChallenge 并且成功的原因。老实说,我不知道这是否是正确的方法,但至少它解决了我的问题。希望这可以帮助。 ?【参考方案2】:

我认为,这是因为您同时添加了两个身份验证,一个使用 jwt,另一个使用此代码。

 var mvcBuilder =
                services.AddMvc(mvcOptions =>
                
                    //only allow authenticated users
                    var policy = new AuthorizationPolicyBuilder()
                        .RequireAuthenticatedUser()
                        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
                        .AddRequirements(new SolidAccountRequirement())
                        .Build();

                    mvcOptions.Filters.Add(new AuthorizeFilter(policy));
                );

只保留 jwt,如果你想添加策略,你可以像这个例子那样做

services.AddAuthorization(options =>
                
                    options.AddPolicy("CreateUser", policy => policy.AddRequirements(
                    new UserLevelRequirement("Admin")
                ));
                )

您不要配置两次授权。

【讨论】:

OnAuthenticationFailed 异常仍然被触发 :(【参考方案3】:

OnAuthenticationFailed 事件将允许引发。但您的 API 将返回 406 Not Acceptable,因为此事件会将 Response ContentType 设置为“text/plain”;。您可以将代码c.Response.ContentType = "text/plain"; 更改为c.Response.ContentType = "application/json";

【讨论】:

【参考方案4】:

我在这里找到了解决方案https://github.com/aspnet/Security/issues/1488

对 AddAuthorization 的调用必须在 MvcCoreBuilder 上,而不是在服务集合上。

services
    .AddMvcCore()
    .AddAuthorization(...)

缺点是 AddMvcCore() 不会添加 AddMvc() 添加的所有服务。查看 AddMvc() 的源代码以了解要添加的附加服务。

编辑 以上对我仍然不起作用。

你可以试试[OverrideAuthentication]

【讨论】:

以上是关于Asp.net core 2.0 Jwt 不会忽略 AllowAnonymous的主要内容,如果未能解决你的问题,请参考以下文章