ASP.NET Core 5.0 JWT 身份验证引发 401 代码 [重复]

Posted

技术标签:

【中文标题】ASP.NET Core 5.0 JWT 身份验证引发 401 代码 [重复]【英文标题】:ASP.NET Core 5.0 JWT authentication is throws 401 code [duplicate] 【发布时间】:2021-08-24 04:15:09 【问题描述】:

我有一个使用 JWT 身份验证的 ASP.NET Core 5.0 API。

我现在想要做的就是读取按钮中的标记

@html.ActionLink("Test","Oper","Home")

它是[Authorize] 标头,并根据我的标准验证它们。我不知道错过了什么,但它总是返回 HTTP 401 代码。

测试添加此代码

app.UseCors(x => x.AllowAnyHeader()
                  .AllowAnyMethod()
                  .WithOrigins("https://localhost:4200"));

错误:

System.InvalidOperationException:CORS 协议不允许同时指定通配符(任何)来源和凭据。如果需要支持凭据,请通过列出各个来源来配置 CORS 策略。

连接终端的pty主机进程无响应,终端可能停止工作

这不是一个 Angular 项目。

这是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(), "Test_modules")),
                RequestPath = "/" + "Test_modules"
            );
            app.UseCookiePolicy();

            app.UseRouting();

            app.UseAuthentication();

            app.UseAuthorization();
            app.UseCors(x => x.AllowAnyHeader()
                              .AllowAnyMethod()
                              .WithOrigins("https://localhost:4200"));

            app.UseEndpoints(endpoints =>
            
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "controller=Home/action=Index/id?");
            );
        
    

这是HomeController.cs - 登录获取 Jwt 令牌就可以了:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using JWTtokenMVC.Models;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.Extensions.Configuration;
using JWTtokenMVC.Models.Test;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;

namespace JWTtokenMVC.Controllers

    public class HomeController : Controller
    
        private IConfiguration _config;

        public HomeController(IConfiguration config)
        
            _config = config;
        

        public IActionResult Index()
        
            return View();
        

        public IActionResult Privacy()
        
            return View();
        

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        
            return View(new ErrorViewModel  RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier );
        

        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.Expires = DateTimeOffset.UtcNow.AddHours(12);//you can set this to a suitable timeframe for your situation
            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);
        
    

这是Test.cshtml

@
    ViewBag.Title = "Home Page";


@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@functions
    public string GetAntiXsrfRequestToken()
    
        return Xsrf.GetAndStoreTokens(Context).RequestToken;
    


<input type="hidden" id="RequestVerificationToken"
       name="RequestVerificationToken" value="@GetAntiXsrfRequestToken()">


<form method="post" asp-antiforgery="false">
<!--form -->


    <div>
        <span style="color:red">@ViewBag.Msg</span>
    </div>

    <div class="col-md-4 select-outline">
</div>
    <button type="button" class="btn btn-light">@Html.ActionLink("Test","Oper","Home")</button>
  <button type="button" class="btn btn-light">@Html.ActionLink("TestLogin","Login","Home")</button>

</form>

最后是Oper.cshtml:

@using JWTtokenMVC.Models.Test
@model List<Test_SHOW>

@
    ViewBag.Title = "test";


<h2>Test List</h2>

<table class="table table-hover">
    <tr>
        <th>
          number
        </th>
        <th>
           name
        </th>
        <th>
            mail
        </th>
    </tr>
    @foreach (var item in Model)
        
        <tr>
             <td>
                @Html.DisplayFor(modelItem => item.number)
            </td>
            <td>
                <span class='text-danger'>@Html.DisplayFor(modelItem => item.name)</span>
            </td>
            <td>
                  <span class='text-danger'>@Html.DisplayFor(modelItem => item.mail)</span>
            </td>
        </tr>
    
</table>

这是我的appsettings.json 文件:


  "Logging": 
    "LogLevel": 
      "Default": "TestInformation",
      "Microsoft": "TestWarning",
      "Microsoft.Hosting.Lifetime": "TestInformation"
    
  ,
  "AllowedHosts": "*",
  "Jwt": 
    "Key": "TestProdigy",
    "Issuer": "Test.mail.com"
  

【问题讨论】:

对不起,已经更新了代码 在 appsettings.json 中更改你的 Jwt 键,我认为应该是 16 个字符的长度。 是的,new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"])); SecurityAlgorithms.HmacSha256 需要 16 个字符长 .. 也遇到了这个问题并在 idx10603 SO question 上进行了审核 试试app.UseCors("CorsPolicy"); i ref [idx10603 SO question](***.com/questions/47279947/…) 已经更新了代码,但要读取按钮@Html.ActionLink("Test","Oper","Home") 中的标记和它是 [Authorize] 标头并根据我的标准验证它们。我不知道错过了什么,但它总是返回 HTTP 401 代码。 【参考方案1】:

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

关于您的 401 Unauthorized Responses

这些可能与 CORS 无关。

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

这意味着,为了导航/调用Oper(),您需要确保“授权:承载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 或在此范围内为空。此声明和分配不会发生预处理或后处理。如果您打算支持 Authorization Header 和 Cookie,则应在此 OnMessageReceived 分配的委托中实现该条件。

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

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

【讨论】:

@Brett Caswell answer 非常感谢解决我的问题【参考方案2】:

正如@Yinkiu 提到的。您必须使用app.UseCors("CorsPolicy"),因为它不是您已经提到的角度项目。安装 NuGet 包 Microsoft.AspNetCore.Cors

我已经回答了你的一个与此相关的问题。这是另一个过程。你实际上不能同时使用AllowAnyOrigin()AllowCredentials()。但是如果你想要AllowCredentials()AllowAnyOrigin() 一起使用这个SetIsOriginAllowed(Func&lt;string,bool&gt; predicate)

About IsOriginAllowed

services.AddCors(options =>
            
                options.AddPolicy("CorsPolicy",
                    builder => builder
                    .AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    );

                options.AddPolicy("signalr",
                    builder => builder
                    .AllowAnyMethod()
                    .AllowAnyHeader()

                    .AllowCredentials()
                    .SetIsOriginAllowed(hostName => true));
            );

【讨论】:

以上是关于ASP.NET Core 5.0 JWT 身份验证引发 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 的身份验证 - 前端验证