首页 > 编程语言 >ASP.Net8 中使用 JWT 鉴权的异常处理

ASP.Net8 中使用 JWT 鉴权的异常处理

时间:2024-08-27 18:36:52浏览次数:14  
标签:exception ASP JWT context Net8 new true Response

.Net8 中使用 JWT 鉴权的异常处理

自己搭了个学习 Demo 想用 JWT 給后端做鉴权,结果一直报一些奇奇怪怪的异常,最主要是和写业务代码不一样,因为用了官方提供的包很难排查出问题所在,这次有点像以前学Spring的时候,也是一点一点摸着石头过河,最后还是同事帮忙看出来问题在哪的。


问题1:IDX14100: JWT is not well formed, there are no dots (.). The token needs to be in JWS or JWE Compact Serialization Format. (JWS): 'EncodedHeader.EndcodedPayload.EncodedSignature'. (JWE): 'EncodedProtectedHeader.EncodedEncryptedKey.EncodedInitializati

描述的是 JWT 缺少 . 符号,正常来说 JWT 由三部分组成,分别是 Header 、 Payload 和 Sign 组成,因此也就由两个 . 将字符串分割开来。明明我携带的 Token 是符合要求的为啥还报这个问题呢,后来通过 Github 的 Issue 找到了解决方案,参考:更新到 .NET 8 后的 SecurityTokenMalformedException ·问题 #52286 ·dotnet/aspnetcore (github.com)judilsteve回答了解决方法也就是在配置中开启

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
       .AddJwtBearer(opts =>
       {
            opts.UseSecurityTokenValidators = true;
       });

答主也说了这个 exception 真的很容易产生误解,也可能是我太菜了没看开发文档直接上手撸的原因。


问题2:Microsoft.IdentityModel.Tokens.SecurityTokenInvalidIssuerException: IDX10211: Unable to validate issuer. The 'issuer' parameter is null or whitespace

这里提供一个解析 JWT 的地址jwt.ms: Welcome!,如果解析完的 JWT 确实缺少这几个必要的参数,那需要补全了再进行鉴权。

这里有一个非常疑惑的点,也就是 issuer 在正常的 JWT中解析完应该是 iss,audiences 应该是 aud,过期时间应该是exp才对,但这里报错确是全称,于是我就将这几个参数以全程的形式全都显式的添加到了 Claims 里面,没想到还是报这个 error,为了解决这个问题查了很多文章和 issue 都没解决,后来同事在她的电脑上帮我测试了下发现编写的代码一定点问题都没有,权限也OK。那我项目怎么解析 JWT 的时候 Claims 总会少那么几个,后来把思路放到包管理上了,发现自己不知道什么时候装了个包,名称为Microsoft.IdentityModel.Tokens,如果你恰好也安装了Microsoft.AspNetCore.Authentication.JwtBearer这个包,那么可能倒霉的踩到这个坑了,我开始还看说都是微软开发的包应该没什么问题,结果就是。。。,貌似是 JwtBearer 这个包中包含了 Tokens 这个包,导致装了两个 Tokens 这个包产生冲突了(我猜的),最后解决办法就是卸载了Microsoft.IdentityModel.Tokens这个包就解决了 Claims 缺失的问题。怪自己太菜了,以后有机会看看源码是怎么造成的这种现象。


最后附上生成 JWT 的代码及排查异常时的监听事件

// 解析 JWT
var handler = new JwtSecurityTokenHandler();
                var jwtToken = handler.ReadToken("<YOUR JWT TOKEN >") as JwtSecurityToken;
                if (jwtToken != null)
                {
                    Console.WriteLine(jwtToken.Claims.First(c => c.Type == JwtRegisteredClaimNames.Exp).Value);
                }

var token = new JwtSecurityToken(
            issuer: _configuration["Jwt:Issuer"],	// 从appsetting中读取配置文件
            audience: _configuration["Jwt:Audience"],
            claims: new List<Claim>
            {
                new Claim(ClaimTypes.Name, user.Username.ToString()),
                new Claim(ClaimTypes.Role, "Permission"),
                new Claim("Permission", "1"),
                
                // 显式添加 exp 声明
                // new Claim(JwtRegisteredClaimNames.Exp, ((DateTime.UtcNow.AddDays(1) - new DateTime(1970, 1, 1)).TotalSeconds).ToString("F0"))
            },
            expires:DateTime.UtcNow.AddDays(1),
            notBefore: DateTime.Now,
            signingCredentials: credentials);
// 配置身份验证
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true, // 发行人是否合法
            ValidateAudience = true, // 目标受众是否正确
            ValidateLifetime = true, // 有效期是否过期
            ValidateIssuerSigningKey = true, // 签名密钥是否正确
            ValidIssuer = builder.Configuration["Jwt:Issuer"], // 从应用配置中读取发行人
            ValidAudience = builder.Configuration["Jwt:Audience"], // 从应用配置中读取标识符
            IssuerSigningKey =
                new SymmetricSecurityKey(
                    Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecurityKey"] ?? "")), // 通过应用程序配置中获取密钥字符串
            ClockSkew = TimeSpan.FromSeconds(30), // 允许的时间偏差
            RequireExpirationTime = true // JWT是否包含过期时间
        };

        options.UseSecurityTokenValidators = true;
        // 捕获并处理认证事件
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                var exception = context.Exception;
            
                if (exception is SecurityTokenExpiredException)
                {
                    context.Response.Headers.Add("Token-Expired", "true");
                }
                else if (exception is SecurityTokenInvalidSignatureException)
                {
                    context.Response.Headers.Add("Token-Invalid-Signature", "true");
                }
                else if (exception is SecurityTokenInvalidAudienceException)
                {
                    context.Response.Headers.Add("Token-Invalid-Audience", "true");
                }
                else if (exception is SecurityTokenInvalidIssuerException)
                {
                    context.Response.Headers.Add("Token-Invalid-Issuer", "true");
                }
                else if (exception is SecurityTokenNoExpirationException)
                {
                    context.Response.Headers.Add("Token-No-Expiration", "true");
                }
                else
                {
                    // 添加日志记录
                    Console.WriteLine(exception.ToString(), "An unhandled exception occurred during authentication.");
                }
            
                return Task.CompletedTask;
            },
            OnTokenValidated = context =>
            {
                var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
            
                if (claimsIdentity != null && !claimsIdentity.HasClaim(c => c.Type == "required_claim"))
                {
                    context.Fail("Unauthorized"); // 如果校验失败,终止请求
                }
            
                return Task.CompletedTask;
            },
            OnChallenge = context =>
            {
                // 自定义处理失败挑战的响应
                context.HandleResponse();
                context.Response.StatusCode = 401;
                context.Response.ContentType = "application/json";

                // 获取异常信息
                var errorInfo = context.Error;
                var errorDescription = context.ErrorDescription;

                var result =
                    JsonConvert.SerializeObject(new { error = errorInfo, error_description = errorDescription });
                context.Response.WriteAsync(result);
                return Task.CompletedTask;
            }
        };
    });

标签:exception,ASP,JWT,context,Net8,new,true,Response
From: https://www.cnblogs.com/junun/p/18383302

相关文章

  • PyJWT 和 python-jose 在处理JWT令牌处理的时候的差异和具体使用
    PyJWT和python-jose是两个用于处理JSONWebTokens(JWT)的Python库。它们都有助于生成、解码、验证和管理JWT,但它们在功能范围和设计哲学上有一些重要的区别。本篇介绍它们之间的一些差异,以及在项目中使用FastAPI+ python-jose 来处理访问令牌的生成以及一些例子代码供......
  • JWT的一种实现
    网上看到的JWT的一种实现。//c#Hmacsha256protectedStringHMacSha256Hash(Stringkey,Stringmessage){varkeyBytes=Encoding.UTF8.GetBytes(key);using(varhmacsha256=newHMACSHA256(keyBytes))......
  • C#面:解释ASP.NET MVC中的打包压缩
    在ASP.NET MVC(Model-View-Controller)架构中,打包压缩是指将前端资源文件(如CSS、JavaScript文件)进行合并和压缩,以减少网络传输的数据量,提高网页加载速度的一种技术。打包压缩的主要目的:减少HTTP请求的次数和文件大小在传统的Web开发中,每个页面通常会引用多个CSS和JavaScript......
  • C#/asp.net-智能制造业ERP系统-89973(免费领源码+开发文档)可做计算机毕业设计JAVA、PHP
    C#(asp.net)智能制造业ERP系统摘 要随着互联网趋势的到来,各行各业都在考虑利用互联网将自己推广出去,最好方式就是建立自己的互联网系统,并对其进行维护和管理。在现实运用中,应用软件的工作规则和开发步骤,采用C#技术建设智能制造业ERP系统。本设计主要实现集人性化、高效率......
  • Gin框架使用jwt
    做个笔记,没有连数据库,没有存redis,只是demopackagemainimport( "errors" "fmt" "log" "net/http" "strings" "time" "github.com/dgrijalva/jwt-go/v4" "github.com/gin-gonic/gin")typ......
  • Asp .Net Core 学习笔记
    Startup类ConfigureServices方法注册服务,并通过依赖注入(DI)或者ApplicationServices在整个应用中使用服务使用IServiceCollection的各种Add{Service}进行注册,例如,AddDbContext、AddDefault、AddEntityFrameworkStores和AddPages在Configure方法配置应用服务之前,由主机......
  • 使用Jaspyt对配置文件密码保护
    1、添加依赖<dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-boot-starter</artifactId><version>3.0.5</version></dependency>......
  • .net8 的webapi部署到华为云的操作
    首先还是打包到文件夹:我的服务器是X64的centos8系统,所以我的配置如图: 发布后,我没有用它的dockerfile,而是用的docker-compose去编写的docker脚本,如下:services:ticket_manager_Api:#服务名称container_name:ticket_manager_Api#容器名称hostname:ticke......
  • ASP: Response 对象 错误 'ASP 0251 : 80004005' 解决办法
    ASP:Response对象错误'ASP0251:80004005'解决办法 Response对象错误'ASP0251:80004005'超过响应缓冲区限制 这种情况一般是因为需要输出的网页内容太大了,由于asp在输入内容到客户的浏览器上之前,会把需要输出的全部内容先输出到缓存区,这个缓存区一般默认大小......
  • .Net8 部署到IIS 10 上需要注意的点
    现在大部分项目都上云了,而且是linux的系统,这部分我下一篇再讲,这次讲一下如何部署到iis10,首先项目点击发布-》目标框架.net8部署模式是独立,目标运行时是win-x64,你也可以选择部署模式为依赖框架,目标运行时选择可移植,但是这样的话要注意IIS的应用程序池选择启用32位应用程序,如果是......