ASP.NET Core 5.0 JWT 身份验证总是抛出 HTTP 401 代码
ASP.NET Core 5.0 JWT authentication is always throws HTTP 401 code
我想在 ASP.NET Core 中实现 JWT-based 安全性。现在我想让它做的就是读取按钮 @Html.ActionLink("Test","Oper","Home")
中的令牌,授权 header 并根据我的标准验证它们。我不知道错过了什么,但它总是返回 HTTP 401 代码。
文件HomeController.cs
private string GenerateJSONWebToken(UserPaul userinfo)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub,userinfo.Username),
new Claim(JwtRegisteredClaimNames.Email,userinfo.Email),
new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()),
};
var token = new JwtSecurityToken(
issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(10),
signingCredentials: credentials
);
var encodetoken = new JwtSecurityTokenHandler().WriteToken(token);
var cookieOptions = new CookieOptions();
cookieOptions.HttpOnly = true;
cookieOptions.Expires = DateTime.Now.AddMinutes(1);
//cookieOptions.Domain = Request.Host.Value;
cookieOptions.Path = "/";
Response.Cookies.Append("jwt", encodetoken, cookieOptions);
return encodetoken;
}
[HttpPost]
public IActionResult Login()
{
string AccountNumber="TestUser";
JWTtokenMVC.Models.TestContext userQuery = new JWTtokenMVC.Models.TestContext();
var query = userQuery.Testxxxx.Where(N => N.UserId ==AccountNumber).FirstOrDefault();
IActionResult response = Unauthorized();
if (query != null)
{
var tokenStr = GenerateJSONWebToken(query);
response = Ok(new { token = tokenStr });
}
return response;
}
[Authorize]
[HttpGet("Home/Oper")]
public IActionResult Oper()
{
var authenticationCookieName = "jwt";
var cookie = HttpContext.Request.Cookies[authenticationCookieName];
List<Test_SHOW> sHOWs = new List<Test_SHOW>();
JWTtokenMVC.Models.Test.TestContext userQuery= new JWTtokenMVC.Models.Test.TestContext();
var query = userQuery.Test.Select(T => new Test_SHOW
{number= T.number,name= T.name,mail= T.mail}).OrderBy(o => o.Iid);
sHOWs.AddRange(query);
return View("Views/Home/Oper.cshtml", sHOWs);
}
这是Startup.cs代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.FileProviders;
using System.IO;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
namespace JWTtokenMVC
{
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 void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials().Build());
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.IncludeErrorDetails = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType ="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
ValidateIssuer = true,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])
)
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "node_modules")),
RequestPath = "/" + "node_modules"
});
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
Startup.cs 图片
Startup.cs 添加 UseAuthentication
所以我假设您正在尝试 asp.net 核心与 angular project.I 认为您错过了将客户端 URL 添加到您的 .net 核心项目。 AddCors
对 IServiceCollection 的扩展调用仅注册所有必需的服务,但它不会将 Cors 中间件添加到 HTTP 请求 pipeline.So 添加此代码 app.UseCors(x => x.AllowAnyHeader().AllowAnyMethod().WithOrigins("https://localhost:4200"));
在您的配置中 method.i 认为它是解决您的问题。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
//clarify code
}
else{
//clarify code
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(x =>
x.AllowAnyHeader().AllowAnyMethod().WithOrigins("https://localhost:4200")); //your client side URL.you are missing this unfortunately
app.UseAuthentication();
app.UseAuthorization();
//clarify code
}
更新
安装Microsoft.AspNetCore.Cors
只需删除 AllowCredentials()
即可解决您的问题。
@Patriom Sarkar 的回答解决了您的 CORS issues/Error
关于您的 401 未经授权的响应
这些可能与 CORS 无关。
您在这里遇到的问题是您已将 JwtBearer JSON Web 令牌配置为出现在对 Authorize
端点的请求中。默认情况下,它将使用您的授权请求中存在的 Bearer 令牌 header.
这意味着,为了 navigate/call Oper()
,您需要确保“Authorization: Bearer {token}”具有有效令牌(作为请求 header) .
目前,在您的 Login
处理中,您正在生成令牌并执行 Set-Cookie
以便 user-agent/client 使用令牌作为值创建 'jwt' cookie ;但是,user-agent 不会将该 jwt cookie 作为授权:不记名令牌自动添加到后续请求的 header 中。因此 Authorize
属性端点上的 JwtBearer
配置将无效。
同样值得注意的是,在 Xhr 或 Fetch 操作中设置 headers 在 SPA 框架中是支持和正常的(包括存储的 Cookie jwt_tokens)。但是,您不是在此处执行 Xhr 或 Fetch 请求,而是在执行 html 表单 post workflow/mechanic - 导航 pages/views。在这方面,设置授权 Header client-side 是 (AFAIK) 不可能的。
要在此处支持 page/view 导航,您需要在 server-side 上实施一个解决方案,使用传递的 jwt
cookie 设置令牌。
@Kirk Larkin
在 In ASP.NET Core read JWT token from Cookie instead of Headers 中介绍了该解决方案
.AddJwtBearer(options => {
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
context.Token = context.Request.Cookies["jwt"];
return Task.CompletedTask;
}
};
});
另外
context.Token
在此范围内总是 null
或为空。此声明和赋值不会发生任何预处理或 post 处理。如果您打算支持授权 Header 和 Cookie,您应该在这个 OnMessageReceived
分配的委托中实现该条件。
您可以查看 JwtBearerHandler (aspnetcore 5.0) on GitHub 的默认处理方式。
再次感谢@Kirk Larkin 在对链接问题的回复评论中提供此附加信息。
备注
这个答案最初是针对一个重复的问题提供的:ASP.NET Core 5.0 JWT authentication is throws 401 code
我不确定这与最初的问题有多相关,但我想我会插话。
在这个问题上挣扎了一段时间后,我最终添加了@Brett Caswell 所说的内容来解决我的问题。
services.AddAuthentication(options => {
//add configs
})
.AddJwtBearer(x => {
//add more configs
x.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
context.Token = context.Request.Headers["Authorization"];
return Task.CompletedTask;
},
};
})
这是在意识到我的 JwtAuth 方案正在提供令牌之后,但是一旦将令牌发回进行验证,它就不会在我为项目设置的启动或配置中出现任何问题。将此添加到您的 .AddJwtBearer() 块中,您应该会很好。
我想在 ASP.NET Core 中实现 JWT-based 安全性。现在我想让它做的就是读取按钮 @Html.ActionLink("Test","Oper","Home")
中的令牌,授权 header 并根据我的标准验证它们。我不知道错过了什么,但它总是返回 HTTP 401 代码。
文件HomeController.cs
private string GenerateJSONWebToken(UserPaul userinfo)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub,userinfo.Username),
new Claim(JwtRegisteredClaimNames.Email,userinfo.Email),
new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()),
};
var token = new JwtSecurityToken(
issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(10),
signingCredentials: credentials
);
var encodetoken = new JwtSecurityTokenHandler().WriteToken(token);
var cookieOptions = new CookieOptions();
cookieOptions.HttpOnly = true;
cookieOptions.Expires = DateTime.Now.AddMinutes(1);
//cookieOptions.Domain = Request.Host.Value;
cookieOptions.Path = "/";
Response.Cookies.Append("jwt", encodetoken, cookieOptions);
return encodetoken;
}
[HttpPost]
public IActionResult Login()
{
string AccountNumber="TestUser";
JWTtokenMVC.Models.TestContext userQuery = new JWTtokenMVC.Models.TestContext();
var query = userQuery.Testxxxx.Where(N => N.UserId ==AccountNumber).FirstOrDefault();
IActionResult response = Unauthorized();
if (query != null)
{
var tokenStr = GenerateJSONWebToken(query);
response = Ok(new { token = tokenStr });
}
return response;
}
[Authorize]
[HttpGet("Home/Oper")]
public IActionResult Oper()
{
var authenticationCookieName = "jwt";
var cookie = HttpContext.Request.Cookies[authenticationCookieName];
List<Test_SHOW> sHOWs = new List<Test_SHOW>();
JWTtokenMVC.Models.Test.TestContext userQuery= new JWTtokenMVC.Models.Test.TestContext();
var query = userQuery.Test.Select(T => new Test_SHOW
{number= T.number,name= T.name,mail= T.mail}).OrderBy(o => o.Iid);
sHOWs.AddRange(query);
return View("Views/Home/Oper.cshtml", sHOWs);
}
这是Startup.cs代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.FileProviders;
using System.IO;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
namespace JWTtokenMVC
{
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 void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials().Build());
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.IncludeErrorDetails = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType ="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
ValidateIssuer = true,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])
)
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "node_modules")),
RequestPath = "/" + "node_modules"
});
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
Startup.cs 图片
Startup.cs 添加 UseAuthentication
所以我假设您正在尝试 asp.net 核心与 angular project.I 认为您错过了将客户端 URL 添加到您的 .net 核心项目。 AddCors
对 IServiceCollection 的扩展调用仅注册所有必需的服务,但它不会将 Cors 中间件添加到 HTTP 请求 pipeline.So 添加此代码 app.UseCors(x => x.AllowAnyHeader().AllowAnyMethod().WithOrigins("https://localhost:4200"));
在您的配置中 method.i 认为它是解决您的问题。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
//clarify code
}
else{
//clarify code
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(x =>
x.AllowAnyHeader().AllowAnyMethod().WithOrigins("https://localhost:4200")); //your client side URL.you are missing this unfortunately
app.UseAuthentication();
app.UseAuthorization();
//clarify code
}
更新
安装Microsoft.AspNetCore.Cors
只需删除 AllowCredentials()
即可解决您的问题。
@Patriom Sarkar 的回答解决了您的 CORS issues/Error
关于您的 401 未经授权的响应
这些可能与 CORS 无关。
您在这里遇到的问题是您已将 JwtBearer JSON Web 令牌配置为出现在对 Authorize
端点的请求中。默认情况下,它将使用您的授权请求中存在的 Bearer 令牌 header.
这意味着,为了 navigate/call Oper()
,您需要确保“Authorization: Bearer {token}”具有有效令牌(作为请求 header) .
目前,在您的 Login
处理中,您正在生成令牌并执行 Set-Cookie
以便 user-agent/client 使用令牌作为值创建 'jwt' cookie ;但是,user-agent 不会将该 jwt cookie 作为授权:不记名令牌自动添加到后续请求的 header 中。因此 Authorize
属性端点上的 JwtBearer
配置将无效。
同样值得注意的是,在 Xhr 或 Fetch 操作中设置 headers 在 SPA 框架中是支持和正常的(包括存储的 Cookie jwt_tokens)。但是,您不是在此处执行 Xhr 或 Fetch 请求,而是在执行 html 表单 post workflow/mechanic - 导航 pages/views。在这方面,设置授权 Header client-side 是 (AFAIK) 不可能的。
要在此处支持 page/view 导航,您需要在 server-side 上实施一个解决方案,使用传递的 jwt
cookie 设置令牌。
@Kirk Larkin
在 In ASP.NET Core read JWT token from Cookie instead of Headers 中介绍了该解决方案 .AddJwtBearer(options => {
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
context.Token = context.Request.Cookies["jwt"];
return Task.CompletedTask;
}
};
});
另外
context.Token
在此范围内总是 null
或为空。此声明和赋值不会发生任何预处理或 post 处理。如果您打算支持授权 Header 和 Cookie,您应该在这个 OnMessageReceived
分配的委托中实现该条件。
您可以查看 JwtBearerHandler (aspnetcore 5.0) on GitHub 的默认处理方式。
再次感谢@Kirk Larkin 在对链接问题的回复评论中提供此附加信息。
备注
这个答案最初是针对一个重复的问题提供的:ASP.NET Core 5.0 JWT authentication is throws 401 code
我不确定这与最初的问题有多相关,但我想我会插话。
在这个问题上挣扎了一段时间后,我最终添加了@Brett Caswell 所说的内容来解决我的问题。
services.AddAuthentication(options => {
//add configs
})
.AddJwtBearer(x => {
//add more configs
x.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
context.Token = context.Request.Headers["Authorization"];
return Task.CompletedTask;
},
};
})
这是在意识到我的 JwtAuth 方案正在提供令牌之后,但是一旦将令牌发回进行验证,它就不会在我为项目设置的启动或配置中出现任何问题。将此添加到您的 .AddJwtBearer() 块中,您应该会很好。