net6 项目搭建及引用框架记录(log4net,autofac,exception,api result,jwt,efcore)八使用pfx证书RSA方式生成jwt签名的Token,公钥验证签名

Posted 圆滚滚的胖狸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了net6 项目搭建及引用框架记录(log4net,autofac,exception,api result,jwt,efcore)八使用pfx证书RSA方式生成jwt签名的Token,公钥验证签名相关的知识,希望对你有一定的参考价值。

关于JWT引用的补充

因为Ids5收费的缘故,因此原本打算引用ids的想法被放弃了。只能自己来实现了

生产环境下,肯定需要用证书来产生Token,所以我们先生成证书

本机安装openssl,然后打开openssl带的控制台,

 在openssl 控制台中顺序执行以下命令

具体命令的含义请自行百度

// 进入到准备好的证书文件夹
cd F:\\NET6Demo_WebApi\\NET6Demo_WebApi\\Certificate
// 生成私钥key文件
openssl genrsa -out xxx_rsa_aes_private.key 2048
// 生成公钥key文件
openssl rsa -in xxx_rsa_aes_private.key  -pubout -out xxx_rsa_public.key
// 生成crt文件
openssl req -new -x509 -days 3650 -key xxx_rsa_aes_private.key -out xxx_cert.crt
// 生成pfx文件 需要设置证书密码
openssl pkcs12 -export -in xxx_cert.crt -inkey xxx_rsa_aes_private.key -out xxx.pfx
// 生成cer文件
openssl pkcs12 -nodes -nokeys -in xxx.pfx -passin pass:证书密码 -nokeys -out xxx.cer

 回到vs2022 项目中来,可以看到添加后的证书文件

 记得要设置文件属性

然后,修改appsetting.json文件,添加"Certificates",修改:"JWT"  

 

"Certificates": 
     "CertPath": "Certificate\\\\xxx.pfx",
    "Password": "CdPX05TW8rNiq2IE",
    "PublicCert": "Certificate\\\\xxx.cer"
  ,
  
  "JWT": 
    "Issuer": "com.xxx.authService",
    "Audience": "oauth/com.xxx.authService",    
    "Expires": "7200"
  ,

接下来我们对jwt部分进行改造

修改JwtHelper.cs的CreateJwtToken方法

using CommonCode.Converts;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;

namespace CommonCode.JWT

    /// <summary>
    /// 单例
    /// 用于产生Token和验证Token
    /// </summary>
    public class JwtHelper 
    
        private readonly static JwtSecurityTokenHandler _jwtSecurityTokenHandler = new JwtSecurityTokenHandler();

        /// <summary>
        /// 创建加密JwtToken
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static string CreateJwtToken<T, U>(in T user,in U jwt, string certPath, string certPassword) //修改了此方法
        
            IJwtModel jwtModel = ConvertTo.ConvertModel<U, JwtModel>(jwt);
            var claimList = CreateClaimList<T>(user);


            // 这一句会产生500错误
            //var algorithm = signingKey.PrivateKey.SignatureAlgorithm;
            // 选择加密算法
            var algorithm = SecurityAlgorithms.RsaSha256;

            // 加载pfx证书
            X509Certificate2 authCert = new X509Certificate2(certPath, certPassword);
            X509SecurityKey signingKey = new X509SecurityKey(authCert);
            // 生成Credentials
            //var signingCredentials = new SigningCredentials(signingKey, "RS256");
            var signingCredentials = new SigningCredentials(signingKey, algorithm);
            JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(
               jwtModel.Issuer,     //Issuer
               jwtModel.Audience,   //Audience
               claims: claimList,
               DateTime.Now,                    //notBefore
               DateTime.Now.AddSeconds(jwtModel.Expires),   //expires
               signingCredentials               //Credentials
               );
            string jwtToken = _jwtSecurityTokenHandler.WriteToken(jwtSecurityToken);
            return jwtToken;
        

        public static T GetToken<T>(string Token)
        
            Type t = typeof(T);

            object objA = Activator.CreateInstance(t);
            var b = _jwtSecurityTokenHandler.ReadJwtToken(Token);
            foreach (var item in b.Claims)
            
                PropertyInfo _Property = t.GetProperty(item.Type);
                if (_Property != null && _Property.CanRead)
                
                    _Property.SetValue(objA, item.Value, null);
                

            
            return (T)objA;
        


        /// <summary>
        /// 创建包含用户信息的CalimList
        /// </summary>
        /// <param name="authUser"></param>
        /// <returns></returns>
        private static List<Claim> CreateClaimList<T>(T authUser)
        
            var Class = typeof(T);
            List<Claim> claimList = new List<Claim>();
            foreach (var item in Class.GetProperties())
            
                claimList.Add(new Claim(item.Name, Convert.ToString(item.GetValue(authUser))));
            
            return claimList;
        


    

修改Tokenhelper.cs

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using NET6Demo.IRepository;
using CommonCode.JWT;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using CommonCode.Helper;

namespace NET6Demo.Utility.Token

    public class TokenHelper
    
        
        private readonly IConfiguration _configuration;
        private readonly JwtSecurityTokenHandler _jwtSecurityTokenHandler;
        private readonly ILogger<TokenHelper> _logger;
        private ITokenModel _tokenModel;
        private IUserInfo _user;


        public TokenHelper(
            IConfiguration configuration,
            JwtSecurityTokenHandler jwtSecurityTokenHandler,
            ILogger<TokenHelper> logger,
            ITokenModel tokenModel

            //IUserInfo user
            )
        
            _configuration = configuration;
            _jwtSecurityTokenHandler = jwtSecurityTokenHandler;
            _logger = logger;
            _tokenModel = tokenModel;

           // _user = user;
        

        private static string basePath = AppContext.BaseDirectory; //添加
        // <summary>
        /// 创建加密JwtToken
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public string CreateJwtToken(IUserInfo user) //修改
        
            // 从 appsettings.json 中读取配置,采取pfx证书
            //var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:SecretKey"]));
            var certPath = Path.Combine(basePath, AppsettingHelper.Get("Certificates:CertPath"));
            var certPassowrd = AppsettingHelper.Get("Certificates:Password");
            _tokenModel.Issuer = AppsettingHelper.Get("JWT:Issuer");
            _tokenModel.Audience = AppsettingHelper.Get("JWT:Audience");


            //需要从数据库中读取
            _tokenModel.Expires = Convert.ToDouble(_configuration["JWT:Expires"]);


            return JwtHelper.CreateJwtToken(_user, _tokenModel, certPath, certPassowrd);
        


        public IUserInfo GetToken(string Token)
        
            return JwtHelper.GetToken<IUserInfo>(Token);
        

        public bool VerTokenAsync(string Token)
        
            return true;
        
    

修改program.cs 中关于jwt的部分

#region 添加身份验证JWT

builder.Services.AddSingleton<
    IAuthorizationMiddlewareResultHandler, Net6DemoAuthorizationMiddleware>();


//添加jwt验证:
var publicCert = new X509Certificate2(Path.Combine(basePath, builder.Configuration["Certificates:PublicCert"]));
var publicKey = new X509SecurityKey(publicCert);

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>

    options.TokenValidationParameters = new TokenValidationParameters()
    
        ValidateIssuer = true,
        ValidIssuer = builder.Configuration["JWT:Issuer"],
        ValidateAudience = true,
        ValidAudience = builder.Configuration["JWT:Audience"],
        ValidateLifetime = true,
        IssuerSigningKey = publicKey,
    ;
);
#endregion

修改一下Net6DemoAuthorizationMiddleware

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Http;

namespace NET6Demo.Utility.Authorize

    public class Net6DemoAuthorizationMiddleware : IAuthorizationMiddlewareResultHandler
    
        private readonly AuthorizationMiddlewareResultHandler defaultHandler = new();
        public async Task HandleAsync(
            RequestDelegate next,
            HttpContext context,
            AuthorizationPolicy policy,
            PolicyAuthorizationResult authorizeResult)
        
            
            if(!authorizeResult.Succeeded) //添加成功判断
            
                if (authorizeResult.Forbidden
                && authorizeResult.AuthorizationFailure!.FailedRequirements
                    .OfType<Show404Requirement>().Any())
                
                    // Return a 404 to make it appear as if the resource doesn't exist.

                    throw new KeyNotFoundException();
                

                throw new ApplicationException("Invalid token");
            
        


    
    public class Show404Requirement : IAuthorizationRequirement  

在controller修改GetTestResult,添加TestToken

[AllowAnonymous]
        [HttpGet("GetTest")]
        public async Task<IActionResult> GetTestResult(string userId)
        
            Console.WriteLine("测试一下输出日志");
            _logger.LogInformation("日志输出了");
            _user = _provider.GetService<IUsers>().GetUser(userId);            

            return Ok(_tokenHelper.CreateJwtToken(_user));
            


        
        [Authorize]
        [HttpGet("TestToken")]
        public async Task<IActionResult> TestToken()
        
            Console.WriteLine("输出日志 TestToken");
            _logger.LogInformation("TestToken");
            return Ok();
        

另外,这次对AppsettingHelper文件有了小小的修改

using Microsoft.Extensions.Configuration;

namespace CommonCode.Helper

    public class AppsettingHelper
    
        //private static IConfiguration _config;
        //private static string _basePath;


        //public AppsettingHelper(IConfiguration config)
        //
        //    _config = config;
        //    _basePath = AppContext.BaseDirectory;
        //


        public static string? Get(string key)
        

            var config = getConfigContainer();

            return config[key];
        

        /// <summary>
        /// 读取实体信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="session"></param>
        /// <returns></returns>
        public static List<T> Get<T>(params string[] session)
        
            List<T> list = new List<T>();
            var config = getConfigContainer();
            config.Bind(string.Join(":", session), list);
            return list;
        

        private static IConfiguration getConfigContainer()
        
            var basePath = AppContext.BaseDirectory;

            //引用Microsoft.Extensions.Configuration;
            var Configuration = new ConfigurationBuilder()
            .SetBasePath(basePath)
            .AddJsonFile("appsettings.json")
            .AddJsonFile("appsettings.test.json", true, reloadOnChange: true);

            var config = Configuration.Build();
            return config;
        
    

//要记得注销掉program.cs中的相关代码
//builder.Services.AddSingleton(new CommonCode.Helper.AppsettingHelper(config));

运行代码,在swagger中获取到token

 

 

注意:因为我多次运行,所以截图中的Token可能会不一样,不影响程序本身的运行结果

验证在swager进行

 

 

 

在postman中验证

 

 

 

 

以上是关于net6 项目搭建及引用框架记录(log4net,autofac,exception,api result,jwt,efcore)八使用pfx证书RSA方式生成jwt签名的Token,公钥验证签名的主要内容,如果未能解决你的问题,请参考以下文章

net6 项目搭建及引用框架记录(log4net,autofac,exception,api result,jwt,efcore)二配置log4net

net6 项目搭建及引用框架记录(log4net,autofac,exception,api result,jwt,efcore)六添加身份验证,引入JWT

net6 项目搭建及引用框架记录(log4net,autofac,exception,api result,jwt,efcore)六添加身份验证,引入JWT

net6 项目搭建及引用框架记录(log4net,autofac,exception,api result,jwt,efcore)六添加身份验证,引入JWT

net6 项目搭建及引用框架记录(log4net,autofac,exception,api result,jwt,efcore)一建立项目,使用Swagger

net6 项目搭建及引用框架记录(log4net,autofac,exception,api result,jwt,efcore)一建立项目,使用Swagger