ASP.NET Core 5.0 JWT 身份验证总是抛出 HTTP 401 代码

Posted

技术标签:

【中文标题】ASP.NET Core 5.0 JWT 身份验证总是抛出 HTTP 401 代码【英文标题】:ASP.NET Core 5.0 JWT authentication is always throws HTTP 401 code 【发布时间】:2021-08-22 12:38:43 【问题描述】:

我想在 ASP.NET Core 中实现基于 JWT 的安全性。目前,我想做的就是读取按钮 @html.ActionLink("Test","Oper","Home") 中的令牌,授权标题并根据我的标准验证它们。我不知道错过了什么,但它总是返回 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

【问题讨论】:

请不要将代码包含为图像。用文本替换您的启动图像。 请把代码中使用的设置值也加进去 【参考方案1】:

我不确定这与原始问题的相关性如何,但我想我会插话。

在这个问题上挣扎了一段时间后,我最终添加了@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() 块中,您应该会很好。

【讨论】:

【参考方案2】:

@Patriom Sarkar 的回答解决了您的 CORS 问题/错误

关于您的 401 Unauthorized Responses

这些可能与 CORS 无关。

您在这里遇到的问题是您已将 JwtBearer JSON Web 令牌配置为出现在对您的 Authorize 端点的请求中。默认情况下,它将使用您的授权请求标头中存在的 Bearer 令牌。

这意味着,为了导航/调用Oper(),您需要确保“Authorization: Bearer token”具有有效的令牌(作为请求标头)。

目前,在您的Login 处理中,您正在生成令牌并执行Set-Cookie,以便用户代理/客户端以令牌作为值创建“jwt”cookie;但是,用户代理不会将该 jwt cookie 作为 Authorization: Bearer Token 自动添加到后续请求的标头中。因此,Authorize 属性端点上的 JwtBearer 配置将无效。

另外值得注意的是,在 SPA 框架中支持并正常设置 Xhr 或 Fetch 操作中的标头(包括 Cookie 存储的 jwt_tokens)。但是,您在这里不是在执行 Xhr 或 Fetch 请求,而是在执行 html 表单发布工作流/机制 - 导航页面/视图。在这方面,无法在客户端设置授权标头(AFAIK)。

要在此处支持页面/视图导航,您需要在服务器端实施一个解决方案,该解决方案使用传递的 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 或为空。此声明和分配不会发生预处理或后处理。如果您打算支持授权标头和 Cookie,则应在此 OnMessageReceived 分配的委托中实现该条件。

您可以查看JwtBearerHandler (aspnetcore 5.0) on GitHub 的默认处理方式。

再次感谢 @Kirk Larkin 在对链接问题的回复评论中提供此附加信息。

注意

这个答案最初是在一个重复的问题上提供的:ASP.NET Core 5.0 JWT authentication is throws 401 code

【讨论】:

@Brett Caswell 很抱歉一开始没有把我的问题表达清楚。这个问题已经修改了很多次,所以我开了一个新问题【参考方案3】:

所以我假设您正在尝试使用 Angular 项目的 asp.net 核心。我认为您错过了将客户端 URL 添加到您的 .net 核心项目。 AddCorson 对 IServiceCollection 的扩展调用只是注册了所有必需的服务,但它不会将 Cors 中间件添加到 HTTP 请求管道中。所以在您的配置方法中添加此代码 app.UseCors(x =&gt; x.AllowAnyHeader().AllowAnyMethod().WithOrigins("https://localhost:4200"));。我认为它可以解决您的问题。


 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() 即可解决您的问题。

【讨论】:

添加代码错误:CORS 协议不允许同时指定通配符(任何)来源和凭据。如果需要凭据,则通过列出各个来源来配置 CORS 策略 错误:连接终端的pty主机进程无响应,终端可能停止工作。 我了解您选择了 VS 代码来构建您的项目。我更新的答案是解决您的第一个问题。但您的第二个问题是您的 IDE(VS Code)。我会推荐你​​这个链接来解决你的第二个问题:-github.com/microsoft/vscode/issues/117956 @Pritom Sarkar answer 非常感谢我的 CORS 问题 @Paul 很高兴知道 :) 顺便说一句,这是与 cors 相关的问题,所以请点击RIGHT 图标接受我的回答。

以上是关于ASP.NET Core 5.0 JWT 身份验证总是抛出 HTTP 401 代码的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 中的 Jwt 令牌身份验证

ASP.NET Core JWT 身份验证受众属性

ASP.NET Core JWT 身份验证更改声明(子)

JWT 身份验证 ASP.NET Core MVC 应用程序

ASP .NET CORE 2.2 JWT 和声明网站的身份验证

ASP.NET Core SPA 中基于 JWT 的身份验证 - 前端验证