ASP.NET Core Authorize属性不适用于JWT

时间:2016-11-17 03:36:35

标签: c# asp.net-core asp.net-core-webapi

我想在ASP.Net Core中实现基于JWT的安全性。我现在要做的就是在Authorization标题中读取持有者令牌,并根据我的标准验证它们。我不需要(也不想)包含ASP.Net Identity。事实上,我试图避免使用尽可能多的MVC添加的东西,除非我真的需要它们。

我创建了一个最小的项目,它演示了这个问题。要查看原始代码,只需查看编辑历史记录即可。我希望此示例拒绝所有/ api / icons请求,除非它们为Authorization HTTP标头提供相应的承载令牌。示例 实际上允许所有请求

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Routing;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System;
using Newtonsoft.Json.Serialization;

namespace JWTSecurity
{
    public class Startup
    {
        public IConfigurationRoot Configuration { get; set; }

        public Startup(IHostingEnvironment env)
        {
            IConfigurationBuilder builder = new ConfigurationBuilder().SetBasePath(env.ContentRootPath);
            Configuration = builder.Build();
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOptions();
            services.AddAuthentication();
            services.AddMvcCore().AddJsonFormatters(options => options.ContractResolver = new CamelCasePropertyNamesContractResolver());
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();
            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("supersecretkey")),
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                }
            });
            app.UseMvc(routes => routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"));
        }
    }
}

控制器/ IconsController.cs

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace JWTSecurity.Controllers
{
    [Route("api/[controller]")]
    public class IconsController : Controller
    {
        [Authorize]
        public IActionResult Get()
        {
            return Ok("Some content");
        }
    }
}

6 个答案:

答案 0 :(得分:44)

找到了它!

主要问题在于这一行:

services.AddMvcCore().AddJsonFormatters(options => options.ContractResolver = new CamelCasePropertyNamesContractResolver());

我注意到,从AddMvcCore()切换到AddMvc(),授权突然开始工作了!在深入了解the ASP.NET source code后,看看AddMvc()做了什么,我意识到我需要第二次调用IMvcBuilder.AddAuthorization()

services.AddMvcCore()
    .AddAuthorization() // Note - this is on the IMvcBuilder, not the service collection
    .AddJsonFormatters(options => options.ContractResolver = new CamelCasePropertyNamesContractResolver());

答案 1 :(得分:16)

您还使用身份验证,它隐式包含Cookie身份验证。可能您使用身份方案登录并导致身份验证成功。

如果不需要身份验证,则删除身份验证(如果您只想要jwt身份验证),否则请为if [ "x${x}" = "x " -o "x${x}" = "x" ]; then 属性指定Bearer方案,如下所示:

Authorize

答案 2 :(得分:2)

对于那些甚至尝试过预览答案但仍未解决问题的人,下面是我所解决的问题。

[Authorize(AuthenticationSchemes="Bearer")]

答案 3 :(得分:2)

我只是遇到了类似的问题,结果发现控制器级别的[AllowAnonymous]属性会覆盖应用于该控制器内任何操作的所有[Authorize]属性。这是我以前不知道的东西。

答案 4 :(得分:1)

找到了解决此问题的完美方法 您的configure services类应如下所示

public void 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.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));

        services.AddIdentity<ApplicationUser, IdentityRole>
        (options => options.Stores.MaxLengthForKeys = 128)
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultUI()
        .AddDefaultTokenProviders();

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();


        services.AddAuthentication(options =>
        {
            //options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            //options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            //options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

        })
        .AddCookie(cfg => cfg.SlidingExpiration = true)
        .AddJwtBearer(cfg =>
        {
            cfg.RequireHttpsMetadata = false;
            cfg.SaveToken = true;
            cfg.TokenValidationParameters = new TokenValidationParameters
            {
                ValidIssuer = Configuration["JwtIssuer"],
                ValidAudience = Configuration["JwtIssuer"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
                ClockSkew = TimeSpan.Zero // remove delay of token when expire
            };
        });


        services.Configure<IdentityOptions>(options =>
        {
            // Password settings  
            options.Password.RequireDigit = true;
            options.Password.RequiredLength = 8;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireUppercase = true;
            options.Password.RequireLowercase = false;
            options.Password.RequiredUniqueChars = 6;

            // Lockout settings  
            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
            options.Lockout.MaxFailedAccessAttempts = 10;
            options.Lockout.AllowedForNewUsers = true;

            // User settings  
            options.User.RequireUniqueEmail = true;
        });

        services.AddAuthentication().AddFacebook(facebookOptions =>
        {
            facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"];
            facebookOptions.AppSecret =  Configuration["Authentication:Facebook:AppSecret"];
        });
        //Seting the Account Login page  
        services.ConfigureApplicationCookie(options =>
        {
            // Cookie settings  
            options.Cookie.HttpOnly = true;
            options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
            options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login  
            options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout  
            options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied  
            options.SlidingExpiration = true;
        });



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

您可以像下面那样对Web API控制器进行身份验证

[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
public class TaskerController : ControllerBase
{
    [HttpGet("[action]")]
    //[AllowAnonymous]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}

并且您可以像下面对于MVC控制器一样使用基于身份的Authorize属性

public class TaskController : Controller
{

    [Authorize]
    public IActionResult Create()
    {
    }
}

关键解决方案是.AddCookie(cfg => cfg.SlidingExpiration = true) 在JWT身份验证之前添加,即.AddJwtBearer(//removed for brevity)将基于Cookie的授权设置为默认值,因此[Authorize]照常工作,每当需要JWT时,都必须使用[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

显式调用它

希望它可以帮助希望将网站作为前端并将移动就绪Web API用作后端的人。

答案 5 :(得分:0)

如果使用自定义方案,则必须使用

[Authorize(AuthenticationSchemes="your custom scheme")]