C# ASP.NET核心授权属性不适用于JWT
我想在ASP.Net内核中实现基于JWT的安全性。现在,我只想让它读取C# ASP.NET核心授权属性不适用于JWT,c#,asp.net-core,asp.net-core-webapi,C#,Asp.net Core,Asp.net Core Webapi,我想在ASP.Net内核中实现基于JWT的安全性。现在,我只想让它读取Authorization头中的承载令牌,并根据我的标准验证它们。我不需要(也不想)包含ASP.Net标识。事实上,我正在尽量避免使用MVC添加的尽可能多的东西,除非我真的需要它们 我创建了一个最小的项目,它演示了这个问题。要查看原始代码,只需查看编辑历史记录。我希望此示例会拒绝对/api/icons的所有请求,除非它们为授权HTTP头提供相应的承载令牌。示例实际上允许所有请求 Startup.cs using Microso
Authorization
头中的承载令牌,并根据我的标准验证它们。我不需要(也不想)包含ASP.Net标识。事实上,我正在尽量避免使用MVC添加的尽可能多的东西,除非我真的需要它们
我创建了一个最小的项目,它演示了这个问题。要查看原始代码,只需查看编辑历史记录。我希望此示例会拒绝对/api/icons
的所有请求,除非它们为授权
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?}"));
}
}
}
控制器/iconcontroller.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");
}
}
}
您也在使用身份验证,它隐式地包含cookie身份验证。可能是您使用identity scheme登录,导致身份验证成功 如果不需要身份验证(如果您只需要jwt身份验证),请删除该身份验证,否则请为
Authorize
属性指定Bearer
方案,如下所示:
[Authorize(ActiveAuthenticationSchemes = "Bearer")]
找到了强> 主要问题在这方面:
services.AddMvcCore().AddJsonFormatters(options => options.ContractResolver = new CamelCasePropertyNamesContractResolver());
我注意到,通过从AddMvcCore()切换到AddMvc(),授权突然开始工作!在深入研究之后,为了了解AddMvc()
的作用,我意识到我需要第二次调用IMvcBuilder.AddAuthorization()
对于那些甚至尝试了预览答案但没有解决问题的人,下面是我的案例中问题是如何解决的
[Authorize(AuthenticationSchemes="Bearer")]
找到了这个问题的完美解决方案 您的配置服务类应该如下所示
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);
}
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
public class TaskerController : ControllerBase
{
[HttpGet("[action]")]
//[AllowAnonymous]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
关键的解决方案是.AddCookie(cfg=>cfg.SlidingExpiration=true)
在JWT身份验证之前添加,即.AddJwtBearer(//为简洁起见已删除)
将基于Cookie的授权设置为默认,因此[Authorize]照常工作,每当需要JWT时,您必须使用[Authorize(authorizationSchemes=JWTbearDefaults.authorizationScheme)]显式调用它。
希望它能帮助那些希望将网站作为前端,将支持移动的Web API作为后端的人。如果您使用的是自定义方案,则必须使用
[Authorize(AuthenticationSchemes="your custom scheme")]
我刚刚遇到了一个类似的问题,结果发现控制器级别的
[AllowAnonymous]
属性会覆盖应用于该控制器内任何操作的任何[Authorize]
属性。这是我以前不知道的。我明天回去工作时会试试,希望你是对的。注意,我使用identity的原因纯粹是为了隐藏/抽象密码散列。您会建议什么替代方案?推荐使用标识解决方案。如果你需要,那就去吧。据我所知,您希望使用identity和jwt可选。在这种情况下,您应该指定要限制的身份验证方案。例如,如果只需要jwt身份验证,则应使用承载方案。使用jwt不需要ActiveAuthenticationSchemes。只要您正确发送令牌,它就可以不使用。@SledgeHammer当然可以,通常您不需要它。但是我假设问题是由身份验证引起的,并且假设OP只想使用jwt身份验证。你是我的Hero。你能展示你应用authorize属性的方法和类吗?你的jwt令牌中间件在哪里?您的服务正在通过/token端点或类似的东西正确地生成JWT?在这一点上,我不关心生成令牌,我只关心拒绝任何在头中不提供令牌的内容。您现在拥有的与我拥有的非常相同,只是我拥有validateIsuer=true和ValidateAudience=true,但我认为这些都不是必需的。我没有服务。AddAuthentication();也可以。是的,我只是试着把它们加进去,没什么区别。一定有什么东西我遗漏了……这也让我明白了(因为我使用的是AddMvcCore
)。对AddAuthorization()
的调用添加了在控制器上查找Authorize/AllowAnonymous并添加适当策略的。如果您将它们用于请求验证,请不要忘记AddDataAnnotations
!Authorize属性在使用服务后开始工作。AddMvcCore().AddAuthorization()
我想您可以在@HoqueMDZahidul检查这个答案。目的是使用MvcCore
生成一个最小的示例。您链接到的答案显示了一个正常工作的asp net配置,但它没有显示使令牌身份验证正常工作的最低配置。如果我只想让身份验证工作,我可以继续使用.AddMvc()
而不是.AddMvcCore()
。您的回答没有针对原始问题问题问题中的一个关键点是,我不需要(也不希望)包含ASP.Net标识
。使用标准配置设置Asp.Net非常简单,但目标是创建一个使用MvcCore和JWTAuthentication的最小项目。答案包括两种类型的身份验证机制,您可以根据需要跳过其中任何一种。您只需注释掉您不需要的部分@andrewwilliamsoni这不仅仅是注释掉不需要的部分的问题。您的ConfigureServices
方法调用.AddMvc()
而不是.AddMvcCore()
。我的整个问题都是基于使用.AddMvcCore()
时身份验证不起作用,我必须通读.AddMvc()
的源代码才能找出区别。当您提供答案时,应该包括回答问题所需的最小更改量。否则,用户仍然需要确定代码的哪一部分解决了问题。我很感激您希望与他人分享您的经验-
public class TaskController : Controller
{
[Authorize]
public IActionResult Create()
{
}
}
[Authorize(AuthenticationSchemes="your custom scheme")]