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的主要内容,如果未能解决你的问题,请参考以下文章

ASP.net Core 2.0 JWT 令牌刷新

Asp.net Core 2.0 基本 JWT 缺少 ApplicationCookie

asp.net core 2.0 授权在身份验证(JWT)之前触发

asp.net core 2.0 web api基于JWT自定义策略授权

ASP.NET Core 2.0 JWT 验证失败并出现“用户授权失败:(null)”错误

如何在 asp.net core 2.0 中允许具有不同发行者的多个 JWT