.NET core JWT身份认证实现

1.概括

从原理上来说jwt实现的身份认证,实际上就是加密解密的过程放到http请求中的一个过程,服务端将特定信息加密,编码后返回给客户端,客户端在请求的过程中将这段经过加密编码的信息放到请求头中,服务端在收到请求的时候,根据头信息特定字段来判断该请求的来源是否是本站点的正常请求,所对应的系统用户等.所以从这个角度看jwt是分为两部分的,第一部分是加密编码生成串,第二部分就是验证.在实际工程中一般也会涉及到续期,什么时机要对客户端的jwt串传出即将过期指令,什么时机客户端用新的jwt串来执行新请求,是一个工程问题,解决方式不只一种,如何做取决于实际情况,当然还会涉及到token过期,信息安全等等一系列问题,本文将针对jwt使用过程中的所有问题展开讨论,给出一个安全可靠的jwt实践方案.

2.jwt是什么

RFC 7519 - JSON Web Token (JWT)

JSON Web Token (JWT) 是一种紧凑的、URL 安全的表示方式
要求在两方之间转让。JWT 中的声明
被编码为 JSON 对象,用作 JSON 的有效负载
Web 签名 (JWS) 结构或作为 JSON Web 的明文
加密 (JWE) 结构,使声明能够数字化
使用消息验证码进行签名或完整性保护
(MAC) 和/或加密。

jwt 的定义如上, JSON Web Tokens - jwt.io中有一个在线的jwt调试器,后边内容有多处会用到jwt调试器来验证我们的jwt实现.

3.基础实现

1.创建项目

mkdir Hero
cd Hero
mkdir Hero.Api
mkdir Hero.Jwt
cd Hero.Api
dotnet new webapi -f net5.0
cd ../Hero.Jwt
dotnet new classlib -f net5.0
cd ../
dotnet new sln
dotnet sln Hero.sln add Hero.Api/Hero.Api.csproj
dotnet sln Hero.sln add Hero.Jwt/Hero.Jwt.csproj

2.定义jwt结构中需要用到的信息,放到一个类中

首先我们知道jwt中一定会有的字段有Issuer,Audience,另外jwt有过期时间,所以要有代表生命周期的Lifetime,表示续期的RenewalTime,然后是头字段,是否验证失效时间,是否验证签名等,于是就有了如下结构. JwtConfig.cs

在Hero.Jwt中定义一个叫做JwtConfig的类,表示针对jwt的所有配置信息

    internal class JwtConfig
    {
        public string Issuer { get; set; }
        public string Audience { get; set; }
        /// <summary>
        /// 签名key
        /// </summary>
        public string SecretKey { get; set; }
        /// <summary>
        /// 生命周期
        /// </summary>
        public int Lifetime { get; set; }
        /// <summary>
        /// 续期时间
        /// </summary>
        public int RenewalTime { get; set; }
        /// <summary>
        /// 是否验证生命周期
        /// </summary>
        public bool ValidateLifetime { get; set; }
        /// <summary>
        /// 验证头字段
        /// </summary>
        public string HeadField { get; set; }
        /// <summary>
        /// 新Token的Head字段
        /// </summary>
        public string ReTokenHeadField { get; set; }
        /// <summary>
        /// jwt验证前缀
        /// </summary>
        public string Prefix { get; set; }
        /// <summary>
        /// 忽略验证的url
        /// </summary>
        public List<string> IgnoreUrls { get; set; }
    }

有关jwt的方法应该也只有两个,一个是根据一些信息Claims获取token,另外一个就是验证token是否有效,如果有效返回jwt中的自定义Claims.紧接着定义一个IJwt的接口定义这两个方法

    public interface IJwt
    {
        /// <summary>
        /// 生成Token
        /// </summary>
        /// <param name="Claims">需要传递到jwt中的自定义信息</param>
        /// <returns></returns>
        string GetToken(IDictionary<string, object> Claims);
        /// <summary>
        /// 验证Token
        /// </summary>
        /// <param name="Token">待验证的字符串</param>
        /// <param name="Claims">得到传入Claims信息</param>
        /// <returns></returns>
        bool ValidateToken(string Token, out Dictionary<string, object> Claims);
    }

3.裸奔式的实现

首先是实现IJwt

要用到configration中的一些东西 和 jwt验证所以引入下边这三个类库 ,也是jwt的所有依赖

工具->nuge包管理器->程序包管理器控制台

install-package Microsoft.Extensions.Configuration.Abstractions //config
install-package Microsoft.Extensions.Configuration.Binder //config
install-package Microsoft.AspNetCore.Http.Abstractions//中间件中用到
install-package System.IdentityModel.Tokens.Jwt //jwt底层库
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;

namespace Hero.Jwt
{
    public class Jwt : IJwt
    {
        private readonly JwtConfig _jwtConfig = new JwtConfig();
        private byte[] signingKey;
        public Jwt(IConfiguration configration)
        {
            configration.GetSection("Jwt").Bind(_jwtConfig);
            GenrateSigningKey();
        }
        /// <summary>
        /// 生成签名
        /// 需要用到byte[64]位固定长度签名,所以这里用签名算法做是最为稳妥的.
        /// </summary>
        private void GenrateSigningKey()
        {
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            byte[] keyByte = encoding.GetBytes(_jwtConfig.SecretKey);
            byte[] messageBytes = encoding.GetBytes(_jwtConfig.SecretKey);
            using (HMACSHA512 hmacsha512 = new HMACSHA512(keyByte))
            {
                signingKey = hmacsha512.ComputeHash(messageBytes);
            }
        }
        /// <summary>
        /// 生成Token
        /// </summary>
        /// <param name="Claims"></param>
        /// <returns></returns>
        public string GetToken(IDictionary<string, object> Claims)
        {
            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
            SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor
            {
                Issuer = _jwtConfig.Issuer,
                Audience = _jwtConfig.Audience,
                NotBefore = DateTime.Now,
                Expires = DateTime.Now.AddMinutes(_jwtConfig.Lifetime),
                Claims =Claims,
                //不同的签名算法要求的key长度是不同的,根据算法为SymmetricSecurityKey传参
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(signingKey),
                                           SecurityAlgorithms.HmacSha512Signature)
            };
            SecurityToken securityToken = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(securityToken);
        }
        public bool ValidateToken(string Token, out Dictionary<string, object> Clims)
        {
            Clims = new Dictionary<string, object>();
            ClaimsPrincipal principal = null;
            if (string.IsNullOrWhiteSpace(Token))
            {
                return false;
            }
            JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
            try
            {
                JwtSecurityToken jwt = handler.ReadJwtToken(Token);
                if (jwt == null)
                {
                    return false;
                }
                TokenValidationParameters validationParameters = new TokenValidationParameters
                {
                    RequireExpirationTime = true,
                    ClockSkew = TimeSpan.Zero,
                    IssuerSigningKey = new SymmetricSecurityKey(signingKey),//签名验证算法
                    ValidateIssuer = true,//是否验证Issuer
                    ValidateAudience = true,//是否验证Audience
                    ValidateLifetime = _jwtConfig.ValidateLifetime,//是否验证失效时间
                    ValidAudience = _jwtConfig.Audience,
                    ValidIssuer = _jwtConfig.Issuer,
                    ValidateIssuerSigningKey = true,//是否验证签名
                };
                principal = handler.ValidateToken(Token, validationParameters, out SecurityToken securityToken);
                foreach (Claim item in principal.Claims)
                {
                    Clims.Add(item.Type, item.Value);
                }
                return true;
            }
            catch (SecurityTokenInvalidLifetimeException ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }
    }
}

紧接着增加一个中间件,用于判断请求头中的携带jwt的字段,在token即将过期时发放新Token

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Hero.Jwt
{
    public class UseJwtMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly JwtConfig _jwtConfig = new JwtConfig();
        private readonly IJwt _jwt;
        public UseJwtMiddleware(RequestDelegate next, IConfiguration configration, IJwt jwt)
        {
            _next = next;
            _jwt = jwt;
            configration.GetSection("Jwt").Bind(_jwtConfig);
        }

        private Regex GetIgnoreUrlsReg(List<string> urls)
        {
            List<string> regStrs = new List<string>();
            foreach (var item in urls)
            {
                regStrs.Add(string.Concat("^", item, ".*$"));
            }
            return new Regex(string.Join("|", regStrs));
        }

        public Task InvokeAsync(HttpContext context)
        {
            if (GetIgnoreUrlsReg(_jwtConfig.IgnoreUrls).Match(context.Request.Path).Success)
            {
                return Validate(context, false);
            }
            else
            {
                return Validate(context, true);
            }
        }
        private Task Validate(HttpContext context, bool ValidateToken)
        {
            //验证token的情况下执行
            if (ValidateToken && context.Request.Headers.TryGetValue(this._jwtConfig.HeadField, out Microsoft.Extensions.Primitives.StringValues authValue))
            {
                var authstr = authValue.ToString();
                if (this._jwtConfig.Prefix.Length > 0)
                {
                    if (!authstr.Contains(this._jwtConfig.Prefix))
                    {
                        context.Response.StatusCode = 401;
                        context.Response.ContentType = "application/json";
                        return context.Response.WriteAsync("{\"Status\":401,\"StatusMsg\":\"权限验证失败\"}");
                    }
                    authstr = authValue.ToString().Substring(this._jwtConfig.Prefix.Length, authValue.ToString().Length - (this._jwtConfig.Prefix.Length));
                }
                if (this._jwt.ValidateToken(authstr, out Dictionary<string, object> Clims))
                {
                    List<string> climsKeys = new List<string>() { "nbf", "exp", "iat", "iss", "aud" };
                    IDictionary<string, object> RenewalDic = new Dictionary<string, object>();
                    foreach (var item in Clims)
                    {
                        if (climsKeys.FirstOrDefault(o => o == item.Key) == null)
                        {
                            if (context.Items.Keys.Contains(item.Key))//如果存在key则清理key;
                            {
                                context.Items.Remove(item.Key);
                            }
                            //将所有自定义的Claims信息写入httpcontext上下文中
                            context.Items.Add(item.Key, item.Value);
                            RenewalDic.Add(item.Key, item.Value);
                        }
                    }
                    //验证通过的情况下判断续期时间
                    if (Clims.Keys.FirstOrDefault(o => o == "exp") != null)
                    {
                        var start = new DateTime(1970, 1, 1, 0, 0, 0);
                        var timespanStart = long.Parse(Clims["nbf"].ToString());//token有效时间的开始时间点
                        var tartDate = start.AddSeconds(timespanStart).ToLocalTime();
                        var o = DateTime.Now - tartDate;//当前时间减去开始时间大于续期时间限制
                        if (o.TotalMinutes > _jwtConfig.RenewalTime)
                        {
                            //执行续期
                            var newToken = this._jwt.GetToken(RenewalDic);
                            context.Response.Headers.Add(_jwtConfig.ReTokenHeadField, newToken);
                        }
                    }
                    return this._next(context);
                }
                else
                {
                    if (ValidateToken == true)
                    {
                        context.Response.StatusCode = 401;
                        context.Response.ContentType = "application/json";
                        return context.Response.WriteAsync("{\"Status\":401,\"StatusMsg\":\"权限验证失败\"}");
                    }
                    else
                    {
                        return this._next(context);
                    }
                }
            }
            else
            {
                if (ValidateToken == true)
                {
                    context.Response.StatusCode = 401;
                    context.Response.ContentType = "application/json";
                    return context.Response.WriteAsync("{\"Status\":401,\"StatusMsg\":\"权限验证失败\"}");
                }
                else
                {
                    return this._next(context);
                }
            }
        }
    }
}

如上在中间件中定义了需要忽略jwt认证的url通过正则表达式做的匹配.对于api项目而言,这种处理方式避免了每个controller上都加特性,相对来说还是比较实用的,在续期时间间隔内,再次用旧Token执行请求就会在响应中增加该会话合法的新Token,客户端看到Head中有特定字段 ReToken 的时候,新请求的Token执行替换就可以了,Token完全过期后或者Token错误返回401错误

紧接着在api项目的appsettings.json中增加jwt的配置节点

 "Jwt": {
    "Issuer": "HeroIssuer",
    "Audience": "HeroAudience",
    "SecretKey": "E5807DDD-F481-444C-B3F7-2DBF5AB5043F",
    "Lifetime": 1440, //单位分钟
    "RenewalTime": 1, //单位分钟,Token续期的时间间隔,10表示超过10分钟再次请求就续期
    "ValidateLifetime": true,
    "HeadField": "Token", //头字段
    "ReTokenHeadField": "ReToken",
    "Prefix": "hero ", //前缀
    "IgnoreUrls": [ //最终会拼接为 ^url1.*$|url2.*$|url3.*$,对请求执行匹配,成功的执行过滤
      "/swagger/",
      "/Auth/GetToken"//获取token
    ]
  }

api项目增加对Hero.Jwt的引用,然后在startup.cs中将中间件注册一下,顺便改一下swagger的配置,以适用jwt

如下:

using System;
using Hero.Jwt;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;

namespace Hero.Api
{
    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.AddTransient<IJwt, Hero.Jwt.Jwt>();//注入IJwt
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.AddSecurityDefinition("hero", new OpenApiSecurityScheme()
                {
                    In = ParameterLocation.Header,
                    Name = "Token",
                    Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入hero {token}(注意两者之间是一个空格)",
                    Type = SecuritySchemeType.ApiKey
                });
                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                     { new OpenApiSecurityScheme
                     {
                          Reference = new OpenApiReference()
                          {
                              Id = "hero",
                              Type = ReferenceType.SecurityScheme
                          }
                     }, Array.Empty<string>() }
                 });
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Hero.Api", Version = "v1" });
            });
        }

        // 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();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Hero.Api v1"));
            }


            app.UseRouting();
            app.UseJwt();//注册中间件

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Controller文件夹中增加用于发放Token的接口和测试接口..

using Hero.Jwt;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace Hero.Api.Controllers
{
    [Route("[controller]/[action]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        private readonly IJwt _jwt;
        public AuthController(IJwt jwt)
        {
            _jwt = jwt;
        }
        [HttpPost]
        public string GetToken(UserInfo userInfo)
        {
            //TODO 用户逻辑判断
            Dictionary<string, object> clims = new Dictionary<string, object>
                {
                    {"UserName", userInfo.UserName }
                };
            var result = _jwt.GetToken(clims);
            return result;
        }
    }
}

验证jwt的接口

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Hero.Api.Controllers
{
    [Route("[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        [HttpGet]
        public string GetString()
        {
            return $"有Token才能请求到信息  这是jwt中写入到httpcontext中的内容:{HttpContext.Items["UserName"]}";
        }
    }
}

4.信息安全版jwt实现

基础版中的实现里有对jwt来源签名的验签过程,但是放在Claims中的信息并不安全,打开jwt.io,输入请求中拿到的jwt串

这里 可以很清楚的看到我们Claims的信息,这样的话,只要别人知道签名的key就能伪造签名,即便说一般获取不了签名,别人可以看到系统中的一些机密信息也是很不好的体验.下边就这个问题,用对称加密和非对称加密两种方式对信息进行加密.

1.对称加密+签名

using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;

namespace Hero.Jwt
{
    public class Jwt : IJwt
    {
        private readonly JwtConfig _jwtConfig = new JwtConfig();
        private byte[] signingKey;
        private byte[] encKey=new byte[32];
        public Jwt(IConfiguration configration)
        {
            configration.GetSection("Jwt").Bind(_jwtConfig);
            GenrateSigningKey();
        }
        /// <summary>
        /// 生成签名
        /// 需要用到512位固定长度签名,所以这里用签名算法做是最为稳妥的.
        /// </summary>
        private void GenrateSigningKey()
        {
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            byte[] keyByte = encoding.GetBytes(_jwtConfig.SecretKey);
            byte[] messageBytes = encoding.GetBytes(_jwtConfig.SecretKey);
            using (HMACSHA512 hmacsha512 = new HMACSHA512(keyByte))
            {
                signingKey = hmacsha512.ComputeHash(messageBytes);
                Buffer.BlockCopy(signingKey, 10, encKey, 0, encKey.Length);
            }
        }
        /// <summary>
        /// 生成Token
        /// </summary>
        /// <param name="Claims"></param>
        /// <returns></returns>
        public string GetToken(IDictionary<string, object> Claims)
        {
            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
            SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor
            {
                Issuer = _jwtConfig.Issuer,
                Audience = _jwtConfig.Audience,
                NotBefore = DateTime.Now,
                Expires = DateTime.Now.AddMinutes(_jwtConfig.Lifetime),
                Claims =Claims,
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(signingKey), SecurityAlgorithms.HmacSha512),//对称加密
                EncryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encKey), SecurityAlgorithms.Aes256KW, SecurityAlgorithms.Aes256CbcHmacSha512)
            };
            SecurityToken securityToken = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(securityToken);
        }
        public bool ValidateToken(string Token, out Dictionary<string, object> Clims)
        {
            Clims = new Dictionary<string, object>();
            ClaimsPrincipal principal = null;
            if (string.IsNullOrWhiteSpace(Token))
            {
                return false;
            }
            JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
            try
            {
                JwtSecurityToken jwt = handler.ReadJwtToken(Token);
                if (jwt == null)
                {
                    return false;
                }
                TokenValidationParameters validationParameters = new TokenValidationParameters
                {
                    RequireExpirationTime = true,
                    ClockSkew = TimeSpan.Zero,
                    IssuerSigningKey = new SymmetricSecurityKey(signingKey),
                    TokenDecryptionKey = new SymmetricSecurityKey(encKey),
                    ValidateIssuer = true,//是否验证Issuer
                    ValidateAudience = true,//是否验证Audience
                    ValidateLifetime = _jwtConfig.ValidateLifetime,//是否验证失效时间
                    ValidAudience = _jwtConfig.Audience,
                    ValidIssuer = _jwtConfig.Issuer,
                    ValidateIssuerSigningKey = true,//是否验证签名
                };
                principal = handler.ValidateToken(Token, validationParameters, out SecurityToken securityToken);
                foreach (Claim item in principal.Claims)
                {
                    Clims.Add(item.Type, item.Value);
                }
                return true;
            }
            catch (SecurityTokenInvalidLifetimeException ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }
    }
}

在发Token和验证的时候指定对称算法名称即可

2.非对称加密+签名

修改Jwt.cs

using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;

namespace Hero.Jwt
{
    public class Jwt : IJwt
    {
        private readonly JwtConfig _jwtConfig = new JwtConfig();
        private byte[] signingKey;
        private byte[] encKey=new byte[32];
        public Jwt(IConfiguration configration)
        {
            configration.GetSection("Jwt").Bind(_jwtConfig);
            GenrateSigningKey();
        }
        /// <summary>
        /// 生成签名
        /// 需要用到512位固定长度签名,所以这里用签名算法做是最为稳妥的.
        /// </summary>
        private void GenrateSigningKey()
        {
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            byte[] keyByte = encoding.GetBytes(_jwtConfig.SecretKey);
            byte[] messageBytes = encoding.GetBytes(_jwtConfig.SecretKey);
            using (HMACSHA512 hmacsha512 = new HMACSHA512(keyByte))
            {
                signingKey = hmacsha512.ComputeHash(messageBytes);
                Buffer.BlockCopy(signingKey, 10, encKey, 0, encKey.Length);
            }
        }
        /// <summary>
        /// 生成Token
        /// </summary>
        /// <param name="Claims"></param>
        /// <returns></returns>
        public string GetToken(IDictionary<string, object> Claims)
        {
            string pubkeySigning = "MIIBCgKCAQEAuyeviRRcsPGxSUyYnhE8zYFquUc4InAa5K4qw5Y43R/1sm5jWZiG670+aq+y/1Ovx4RXlyBpzwo4ws0FB7YZyPIPRiZZbB9oIeNd19fdPu7U70lSaIfh9TjHW9Kjb40YFnxm6nltY7QXTw2VeISVZApD0DcL8TieD3cgGpKJjo4nhaHNFmRLR80mSOjFrKFoun5iLk1azljHEpVjqVotKF+5o80DzxW7EywoS2YuXYMI7tOIPP33cRLMFxokZSy2BFeeuu4jakDsvTxMaipOCyumbE2/Fd7bo59n1/jgfyP11rR8HtkhdS9SbK0nw6rVEddFxym6TKjczrxIYxuR4QIDAQAB";
            string prikeySigning = "MIIEogIBAAKCAQEAuyeviRRcsPGxSUyYnhE8zYFquUc4InAa5K4qw5Y43R/1sm5jWZiG670+aq+y/1Ovx4RXlyBpzwo4ws0FB7YZyPIPRiZZbB9oIeNd19fdPu7U70lSaIfh9TjHW9Kjb40YFnxm6nltY7QXTw2VeISVZApD0DcL8TieD3cgGpKJjo4nhaHNFmRLR80mSOjFrKFoun5iLk1azljHEpVjqVotKF+5o80DzxW7EywoS2YuXYMI7tOIPP33cRLMFxokZSy2BFeeuu4jakDsvTxMaipOCyumbE2/Fd7bo59n1/jgfyP11rR8HtkhdS9SbK0nw6rVEddFxym6TKjczrxIYxuR4QIDAQABAoIBAHAnCwjhW95pJ61eKkLm34HjIPpglGIGvgb13AiS+AaCxXCkuAKT5Z5VLJcwLNrW4op0YyzcLqv0WylZRL9nP7JsY/zMtF+XvoY4Qx86a4nwA0hVrv2XGDAkU0tSQcByU9H9wIqYM5ZA8IreAAlVolRt1k9q/UwTepyX7XQfBjGXNft0qu3A3gLIpzVn/oH5mD4UNsNDdgJ/s4FebvdGSucM/UWBoDP4TaotN1IV8yBNyFJhBWMkbRLs20dbfgiFqDnRF8+69Ylw6qrokHDZwZRB5/NMiFt+UFmCFc3RbSps2L1tWLOdAQ5MPTELn+1kk26jkcHP7cZXVbOzqHKQkTUCgYEA2LkujXIdOY1LeB5bSkPUND8PtmVgrsiAEfP/nWX629+UIqwwGtGJz9Gt5c3CNvIsGzazHaUE4DI5ru4B+HgU45ifbLMtII5AtdY8oPd0nTwHpF6iSYKmJzPFPdordyxZ36CLzhaVI9mdHRJS84Fj5O1DJ548PANLz5rTLDiJEbsCgYEA3RKwY636eo3sIjHVHTe3gOt+s3Q41P+Lh1v9TiEuZthg3f7tZUY2EQileLxNAjgAK48XMbtgRTLNax688WjjLIQaYQyXl074gJ1Hn/dirNOGQMrYgrxXmM5oBgXqmEA321W9hz9k7JRYw7HqjKSL2+h8GeacXfkFyC9YZiFFMxMCgYBdNhBSn6D4LtAlwpCq+U9chT7hyOpzYiLLFfF7pe/l/1w8KWirMDIgouMzMnL0pOXZcoZJGr9lGdT7aryIPEVnui3fV5TyKpykWJdM+AE82yPCSz1rdni15atQtfP51qZ06x0WL1pHyAGuDkKFHsJzJKS8dm8btKM3kDSBEXPKnwKBgG8bt39Br4Ps1GMTPJLkr9uhgBpdLTsP/GZZe2PLFXEnCvhH6bRep0nEWLXnnaSh1KQP1I5wKCBfOhK+biO+nX6AHmnsVDv9urOZWKgzQ2qtHOpviIWcd0Ibavir/I3sqKYZ35mb6PNmU353avSotoodvFGgL7KjN562/OzHh+n1AoGAFBFnvge50H2FTT2MlKZ53yY27OAuKK2+KUQGK8OIP4fSWvisFzA68cQrejyAxkx4JMMic0OnHgLMi75Noz6aUYDdtRmuzkmxugz96bhFuj1UhH3HqqaLwn8yrw68X4dUD6eSv7sBhL9RJTVz3cuaR1wFaXZfEhfVcdRlvDYfa0k=";
            string pubkeyEncrypt = "MIIBCgKCAQEAxKrM4LqoQ7O37Wiybx/80NUwi9dDc+hP5MWx/uAyaoWsCTE3AXTcANqKR4MWd8FDKpyJn9WrIDaRnl4XuW8992Bpzqo4KbHlefQ6z848ox7M8BCqyiQ0uz5FeezFzhe1FBGpRmw1o1rH3mpjnkjpb81wWY6NyI6k7/ZUIQFX5Sn8g4vnaGNBlnhhd3oh8esM2IhoDcPvK2QXnpCa1o+md7O3VM1J1M2jIVvULaE5dxz2NIUCQnxFCgH/XYKfcEZEwjBubkFK2HP+pPKo0ZX9kfQ2MPOj7l04YTikA2Majym3BL38U/bNSt8rYHg5EIWwwObYxlo6NOWQRFYM9c1fZQIDAQAB";
            string prikeyEncrypt = "MIIEpAIBAAKCAQEAxKrM4LqoQ7O37Wiybx/80NUwi9dDc+hP5MWx/uAyaoWsCTE3AXTcANqKR4MWd8FDKpyJn9WrIDaRnl4XuW8992Bpzqo4KbHlefQ6z848ox7M8BCqyiQ0uz5FeezFzhe1FBGpRmw1o1rH3mpjnkjpb81wWY6NyI6k7/ZUIQFX5Sn8g4vnaGNBlnhhd3oh8esM2IhoDcPvK2QXnpCa1o+md7O3VM1J1M2jIVvULaE5dxz2NIUCQnxFCgH/XYKfcEZEwjBubkFK2HP+pPKo0ZX9kfQ2MPOj7l04YTikA2Majym3BL38U/bNSt8rYHg5EIWwwObYxlo6NOWQRFYM9c1fZQIDAQABAoIBACr8vn2cryzlOp3NFbuOfV9USiE280qBi/0QbWCttrdr8ner5z8NQQ16t2D8OUwB1WGaB8cFGDuZUekQ3hStSRkqXNZMhKwwc11d0gEcLkrlb5xFuF8o3NHUwbDt3Sq4Kd9yINMA0hSbwjZOgOnXPBcxC463xywAafL9n9P7DDBNw17l2ykW7gJtNRy6tf2kjCEfnHEIgXZZ19xp/BfApYAcLb/v9C/tRaWAzWLLTwX9GEOrJ2XjzIUOrdCmdO85VbH3b2bGHWwi2orbD+uvCZOPcCsOyQjUme40JaDQwgkCPTh/9F5/u/Z8sVcCw/mgzoaxWImdCwwQCj49FhT52V0CgYEA/CEL1VlL5Gb2tolhG8hufORzu2Id0uDNKsUZd92hM4Y98tyDnCqE/F3jiv63G0utHSzC3Rt77iiX9LXXV3AP/Zz91LNEm/vICSEeDOFejx7XEsnjsJuoDMZPt+bNu4bFWv0j81fuVyZ9ZPoy4xb+abmkebq9F2QxvcQs2DO+SKMCgYEAx6/FEaGKk10XWCdFEBeI3I3GViDYzSBEEnaI+wSR7VPBZkCt0w2aD4/wrw3pmlTAkN882GDdFzojtEd7t2nXgp03S0F5T5aGD4pFZWaqUYqz/bQ/UxBp0C5qHR084J5XarllTPBzbZ0fo1eY1Cde/mj/TD722o+9hUsmbi0NkFcCgYEAsZRY8FCvmlRG6jQCeH4IC+Ef/lfR56g7+SbPlFQ+aLrhQP+9lq1/8vvx+wECWLBJYqYXLYJhHFHtDQdSf5xHNvpu8XO+HBsPPhbcQngtkKJJG0ulGcvYZf77QOzH9I+syzRGMOu6zBko8okidD3KvQ5q4O38ptAEFMNqTnDLUf8CgYB6k/VfG1DboRuBa6nDdQ74hLcpi8RKNvJSex0fKfECRJXF1RJfKkxWHT/b1ah+qmQDCmZpVRyi83eTZQYW0wwOC8AznB+BsZ7dzz1GP71xjLlslccBkGPD/Zn6AUarg8eZpfD/R+MzeG5BcLZKFVkExyNghI44IGBwgG841sMqxQKBgQD6nSV5kw8RXBlzWfjI/q9hU0f9U4E2ZX66iNrUhlmK12yDXd0l3I4vpffo2HQVHRGP+4LKnX7g+7P6Zrs3GHoOg7EbnYjcReoBNoxwgTx93b6qBEi9yfQNYVPgZIwxMYq62yNlTcAp9CQLAwkOWaTO51viAVICUOmbQelIny9cMA==";
            RSA rsaSigning = RSA.Create();
            //rsaSigning.ExportRSAPrivateKey() 生成私钥
            //rsaSigning.ExportRSAPublicKey() 生成公钥
            rsaSigning.ImportRSAPrivateKey(Convert.FromBase64String(prikeySigning), out int bytesread);
            RSA rsaEncrypt = RSA.Create();
            rsaEncrypt.ImportRSAPublicKey(Convert.FromBase64String(pubkeyEncrypt), out int bytesread1);
            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
            SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor
            {
                Issuer = _jwtConfig.Issuer,
                Audience = _jwtConfig.Audience,
                NotBefore = DateTime.Now,
                Expires = DateTime.Now.AddMinutes(_jwtConfig.Lifetime),
                Claims =Claims,
                //SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(signingKey), SecurityAlgorithms.HmacSha512),//对称加密
                //EncryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encKey), SecurityAlgorithms.Aes256KW, SecurityAlgorithms.Aes256CbcHmacSha512)
                SigningCredentials = new SigningCredentials(new RsaSecurityKey(rsaSigning.ExportParameters(true)), SecurityAlgorithms.RsaSsaPssSha512),//非对称私钥签名,公钥验签
                EncryptingCredentials = new EncryptingCredentials(new RsaSecurityKey(rsaEncrypt.ExportParameters(false)), SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes128CbcHmacSha256)//非对称加密
                
            };
            SecurityToken securityToken = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(securityToken);
        }
        public bool ValidateToken(string Token, out Dictionary<string, object> Clims)
        {
            string pubkeySigning = "MIIBCgKCAQEAuyeviRRcsPGxSUyYnhE8zYFquUc4InAa5K4qw5Y43R/1sm5jWZiG670+aq+y/1Ovx4RXlyBpzwo4ws0FB7YZyPIPRiZZbB9oIeNd19fdPu7U70lSaIfh9TjHW9Kjb40YFnxm6nltY7QXTw2VeISVZApD0DcL8TieD3cgGpKJjo4nhaHNFmRLR80mSOjFrKFoun5iLk1azljHEpVjqVotKF+5o80DzxW7EywoS2YuXYMI7tOIPP33cRLMFxokZSy2BFeeuu4jakDsvTxMaipOCyumbE2/Fd7bo59n1/jgfyP11rR8HtkhdS9SbK0nw6rVEddFxym6TKjczrxIYxuR4QIDAQAB";
            string prikeySigning = "MIIEogIBAAKCAQEAuyeviRRcsPGxSUyYnhE8zYFquUc4InAa5K4qw5Y43R/1sm5jWZiG670+aq+y/1Ovx4RXlyBpzwo4ws0FB7YZyPIPRiZZbB9oIeNd19fdPu7U70lSaIfh9TjHW9Kjb40YFnxm6nltY7QXTw2VeISVZApD0DcL8TieD3cgGpKJjo4nhaHNFmRLR80mSOjFrKFoun5iLk1azljHEpVjqVotKF+5o80DzxW7EywoS2YuXYMI7tOIPP33cRLMFxokZSy2BFeeuu4jakDsvTxMaipOCyumbE2/Fd7bo59n1/jgfyP11rR8HtkhdS9SbK0nw6rVEddFxym6TKjczrxIYxuR4QIDAQABAoIBAHAnCwjhW95pJ61eKkLm34HjIPpglGIGvgb13AiS+AaCxXCkuAKT5Z5VLJcwLNrW4op0YyzcLqv0WylZRL9nP7JsY/zMtF+XvoY4Qx86a4nwA0hVrv2XGDAkU0tSQcByU9H9wIqYM5ZA8IreAAlVolRt1k9q/UwTepyX7XQfBjGXNft0qu3A3gLIpzVn/oH5mD4UNsNDdgJ/s4FebvdGSucM/UWBoDP4TaotN1IV8yBNyFJhBWMkbRLs20dbfgiFqDnRF8+69Ylw6qrokHDZwZRB5/NMiFt+UFmCFc3RbSps2L1tWLOdAQ5MPTELn+1kk26jkcHP7cZXVbOzqHKQkTUCgYEA2LkujXIdOY1LeB5bSkPUND8PtmVgrsiAEfP/nWX629+UIqwwGtGJz9Gt5c3CNvIsGzazHaUE4DI5ru4B+HgU45ifbLMtII5AtdY8oPd0nTwHpF6iSYKmJzPFPdordyxZ36CLzhaVI9mdHRJS84Fj5O1DJ548PANLz5rTLDiJEbsCgYEA3RKwY636eo3sIjHVHTe3gOt+s3Q41P+Lh1v9TiEuZthg3f7tZUY2EQileLxNAjgAK48XMbtgRTLNax688WjjLIQaYQyXl074gJ1Hn/dirNOGQMrYgrxXmM5oBgXqmEA321W9hz9k7JRYw7HqjKSL2+h8GeacXfkFyC9YZiFFMxMCgYBdNhBSn6D4LtAlwpCq+U9chT7hyOpzYiLLFfF7pe/l/1w8KWirMDIgouMzMnL0pOXZcoZJGr9lGdT7aryIPEVnui3fV5TyKpykWJdM+AE82yPCSz1rdni15atQtfP51qZ06x0WL1pHyAGuDkKFHsJzJKS8dm8btKM3kDSBEXPKnwKBgG8bt39Br4Ps1GMTPJLkr9uhgBpdLTsP/GZZe2PLFXEnCvhH6bRep0nEWLXnnaSh1KQP1I5wKCBfOhK+biO+nX6AHmnsVDv9urOZWKgzQ2qtHOpviIWcd0Ibavir/I3sqKYZ35mb6PNmU353avSotoodvFGgL7KjN562/OzHh+n1AoGAFBFnvge50H2FTT2MlKZ53yY27OAuKK2+KUQGK8OIP4fSWvisFzA68cQrejyAxkx4JMMic0OnHgLMi75Noz6aUYDdtRmuzkmxugz96bhFuj1UhH3HqqaLwn8yrw68X4dUD6eSv7sBhL9RJTVz3cuaR1wFaXZfEhfVcdRlvDYfa0k=";
            string pubkeyEncrypt = "MIIBCgKCAQEAxKrM4LqoQ7O37Wiybx/80NUwi9dDc+hP5MWx/uAyaoWsCTE3AXTcANqKR4MWd8FDKpyJn9WrIDaRnl4XuW8992Bpzqo4KbHlefQ6z848ox7M8BCqyiQ0uz5FeezFzhe1FBGpRmw1o1rH3mpjnkjpb81wWY6NyI6k7/ZUIQFX5Sn8g4vnaGNBlnhhd3oh8esM2IhoDcPvK2QXnpCa1o+md7O3VM1J1M2jIVvULaE5dxz2NIUCQnxFCgH/XYKfcEZEwjBubkFK2HP+pPKo0ZX9kfQ2MPOj7l04YTikA2Majym3BL38U/bNSt8rYHg5EIWwwObYxlo6NOWQRFYM9c1fZQIDAQAB";
            string prikeyEncrypt = "MIIEpAIBAAKCAQEAxKrM4LqoQ7O37Wiybx/80NUwi9dDc+hP5MWx/uAyaoWsCTE3AXTcANqKR4MWd8FDKpyJn9WrIDaRnl4XuW8992Bpzqo4KbHlefQ6z848ox7M8BCqyiQ0uz5FeezFzhe1FBGpRmw1o1rH3mpjnkjpb81wWY6NyI6k7/ZUIQFX5Sn8g4vnaGNBlnhhd3oh8esM2IhoDcPvK2QXnpCa1o+md7O3VM1J1M2jIVvULaE5dxz2NIUCQnxFCgH/XYKfcEZEwjBubkFK2HP+pPKo0ZX9kfQ2MPOj7l04YTikA2Majym3BL38U/bNSt8rYHg5EIWwwObYxlo6NOWQRFYM9c1fZQIDAQABAoIBACr8vn2cryzlOp3NFbuOfV9USiE280qBi/0QbWCttrdr8ner5z8NQQ16t2D8OUwB1WGaB8cFGDuZUekQ3hStSRkqXNZMhKwwc11d0gEcLkrlb5xFuF8o3NHUwbDt3Sq4Kd9yINMA0hSbwjZOgOnXPBcxC463xywAafL9n9P7DDBNw17l2ykW7gJtNRy6tf2kjCEfnHEIgXZZ19xp/BfApYAcLb/v9C/tRaWAzWLLTwX9GEOrJ2XjzIUOrdCmdO85VbH3b2bGHWwi2orbD+uvCZOPcCsOyQjUme40JaDQwgkCPTh/9F5/u/Z8sVcCw/mgzoaxWImdCwwQCj49FhT52V0CgYEA/CEL1VlL5Gb2tolhG8hufORzu2Id0uDNKsUZd92hM4Y98tyDnCqE/F3jiv63G0utHSzC3Rt77iiX9LXXV3AP/Zz91LNEm/vICSEeDOFejx7XEsnjsJuoDMZPt+bNu4bFWv0j81fuVyZ9ZPoy4xb+abmkebq9F2QxvcQs2DO+SKMCgYEAx6/FEaGKk10XWCdFEBeI3I3GViDYzSBEEnaI+wSR7VPBZkCt0w2aD4/wrw3pmlTAkN882GDdFzojtEd7t2nXgp03S0F5T5aGD4pFZWaqUYqz/bQ/UxBp0C5qHR084J5XarllTPBzbZ0fo1eY1Cde/mj/TD722o+9hUsmbi0NkFcCgYEAsZRY8FCvmlRG6jQCeH4IC+Ef/lfR56g7+SbPlFQ+aLrhQP+9lq1/8vvx+wECWLBJYqYXLYJhHFHtDQdSf5xHNvpu8XO+HBsPPhbcQngtkKJJG0ulGcvYZf77QOzH9I+syzRGMOu6zBko8okidD3KvQ5q4O38ptAEFMNqTnDLUf8CgYB6k/VfG1DboRuBa6nDdQ74hLcpi8RKNvJSex0fKfECRJXF1RJfKkxWHT/b1ah+qmQDCmZpVRyi83eTZQYW0wwOC8AznB+BsZ7dzz1GP71xjLlslccBkGPD/Zn6AUarg8eZpfD/R+MzeG5BcLZKFVkExyNghI44IGBwgG841sMqxQKBgQD6nSV5kw8RXBlzWfjI/q9hU0f9U4E2ZX66iNrUhlmK12yDXd0l3I4vpffo2HQVHRGP+4LKnX7g+7P6Zrs3GHoOg7EbnYjcReoBNoxwgTx93b6qBEi9yfQNYVPgZIwxMYq62yNlTcAp9CQLAwkOWaTO51viAVICUOmbQelIny9cMA==";
            RSA rsaSigning = RSA.Create();
            rsaSigning.ImportRSAPublicKey(Convert.FromBase64String(pubkeySigning), out int bytesread);
            RSA rsaEncrypt = RSA.Create();
            rsaEncrypt.ImportRSAPrivateKey(Convert.FromBase64String(prikeyEncrypt), out int bytesread1);
            Clims = new Dictionary<string, object>();
            ClaimsPrincipal principal = null;
            if (string.IsNullOrWhiteSpace(Token))
            {
                return false;
            }
            JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
            try
            {
                JwtSecurityToken jwt = handler.ReadJwtToken(Token);
                if (jwt == null)
                {
                    return false;
                }
                TokenValidationParameters validationParameters = new TokenValidationParameters
                {
                    RequireExpirationTime = true,
                    ClockSkew = TimeSpan.Zero,
                    //IssuerSigningKey = new SymmetricSecurityKey(signingKey),
                    //TokenDecryptionKey = new SymmetricSecurityKey(encKey),
                    IssuerSigningKey = new RsaSecurityKey(rsaSigning.ExportParameters(false)),//公钥解密 --签名算法
                    TokenDecryptionKey = new RsaSecurityKey(rsaEncrypt.ExportParameters(true)),//私钥解密--解密算法
                    ValidateIssuer = true,//是否验证Issuer
                    ValidateAudience = true,//是否验证Audience
                    ValidateLifetime = _jwtConfig.ValidateLifetime,//是否验证失效时间
                    ValidAudience = _jwtConfig.Audience,
                    ValidIssuer = _jwtConfig.Issuer,
                    ValidateIssuerSigningKey = true,//是否验证签名
                };
                principal = handler.ValidateToken(Token, validationParameters, out SecurityToken securityToken);
                foreach (Claim item in principal.Claims)
                {
                    Clims.Add(item.Type, item.Value);
                }
                return true;
            }
            catch (SecurityTokenInvalidLifetimeException ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }
    }
}

用非对称加密生成的jwt串更长,限制是被加密内容不能超出密钥长度,也就是Claims的大小有一定限制,一般情况应该是满足的.

  • 19
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YuanlongWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值