ASP.NET Core 5和6的JWT身份验证始终会抛出HTTP 401代码

4
我希望在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


2
请勿将代码作为图片。用文本替换您的启动图像。与编程有关的内容。 - ProgrammingLlama
请在代码中添加所使用的设置值。 - Santosh Karanam
4个回答

2
我猜您正在尝试使用Angular项目的asp.net core。我认为您忘记将客户端URL添加到.net core项目中了。AddCors扩展调用IServiceCollection只注册所有必需的服务,但它不会向HTTP请求管道添加Cors中间件。因此,请在您的Configure方法中添加此代码app.UseCors(x => 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策略。 - Paul
错误:终端的pty主机进程连接无响应,终端可能停止工作。 - Paul
我理解您选择使用VS Code来构建您的项目。我的更新答案是为了解决您的第一个问题。但是您的第二个问题是与您的IDE(VS Code)有关。我将向您推荐此链接以解决您的第二个问题:- https://github.com/microsoft/vscode/issues/117956 - Pritom Sarkar
@Pritom Sarkar的回答非常好地解决了我的CORS问题,非常感谢。 - Paul
@Paul 很高兴知道这个 :) 顺便说一下,这是一个与CORS相关的问题,请通过点击RIGHT图标接受我的答案。 - Pritom Sarkar

1

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

关于您的401未经授权的响应

这些问题可能与CORS无关。

您在这里遇到的问题是,您已配置JwtBearer JSON Web Tokens以存在于请求Authorize端点的请求中。默认情况下,它将使用出现在您的Authorization请求头中的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表单的提交流程/机制 - 导航页面/视图。在这方面,客户端设置授权头部(Authorization Header)是不可能的(据我所知)。
为了支持此处的页面/视图导航,你需要在服务器端实现一个解决方案,使用传递的jwt cookie来设置令牌。
该解决方案由@Kirk Larkin在在ASP.NET Core中从Cookie读取JWT令牌而不是头部中介绍。
    .AddJwtBearer(options => {
            options.Events = new JwtBearerEvents
            {
                OnMessageReceived = context =>
                {
                    context.Token = context.Request.Cookies["jwt"];
                    return Task.CompletedTask;
                }
            };
        });

此外

在这个范围内,context.Token 始终为 null 或空。此声明和赋值不会进行任何预处理或后处理。如果您打算支持授权标头和 Cookie,则应在此 OnMessageReceived 分配的委托中实现该条件性。

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

再次感谢 @Kirk Larkin 在相关问题的回复评论中提供了这些额外信息。

注意

本答案最初是针对重复问题提供的:ASP.NET Core 5.0 JWT 身份验证引发 401 代码


1
@Brett Caswell 对于一开始没有清楚表达我的问题,我很抱歉。由于问题已经修改了多次,所以我开了一个新的问题。 - Paul

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

0
我尝试在asp.net core 6上配置JwtBearer JSON Web Tokens,但返回的HTTP 401代码与之前的asp.net core 5错误相同。 但是,我加入了@Brett Caswell提供的解决方案来解决我的问题。

以下是代码

.AddJwtBearer(options => {
            options.Events = new JwtBearerEvents
            {
                OnMessageReceived = context =>
                {
                    context.Token = context.Request.Cookies["jwt"];
                    return Task.CompletedTask;
                }
            };
        });

我的 asp .net core 6 代码
Program.cs

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
        options.SlidingExpiration = true;
        options.AccessDeniedPath = "/test/";
    })
    .AddJwtBearer(options =>
            {
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        context.Token = context.Request.Cookies["jwtTest"];
                        return Task.CompletedTask;
                    }
                };
                options.IncludeErrorDetails = true;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = false,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = builder.Configuration["JwtTest:Issuer"],
                    ValidAudience = builder.Configuration["JwtTest:Issuer"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtTest:Key"])

                    )
                };

            });

因为我的应用程序使用基于 Cookie 的身份验证和 JWT 令牌身份验证,所以我的控制器添加了 [Authorize(AuthenticationSchemes=JwtBearerDefaults.AuthenticationScheme)] 来进行 JWT 验证。

HomeController

[Authorize(AuthenticationSchemes=JwtBearerDefaults.AuthenticationScheme)]
    [HttpGet("[action]")]
    public ActionResult ShowData()
    {
        var quetTest = dbTestContext.staffTestData.ToList();

        return View("Views/Home/test.cshtml", query);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接