zoukankan      html  css  js  c++  java
  • 手写JWT加解密

    C#生成的JWT Token,java不认,

    看了下java JWT的生成源码,自己动手实现了一个C#版的JWT生成工具

    目的是为了调试线上java的jwt接口

    线上项目请使用成熟的jwt组件

    参考了网上大佬的文章,简单封装一个开箱即用的工具类

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace System
    {
        /// <summary>
        /// 实现JWT加密和校验
        /// </summary>
        public class MyJWTTools
        {
            /// <summary>
            /// 生成JWT
            /// </summary>
            /// <param name="sub">jwt所面向的用户</param>
            /// <param name="exp">jwt的过期时间,这个过期时间必须要大于签发时间</param>
            /// <param name="secretKey">密钥</param>
            /// <param name="SerializePayloadToJsonFunc">参数为payload的类型,返回序列化后的json字符串</param>
            /// <param name="defEncoding">信息部分的编码</param>
            /// <returns></returns>
            public static string CreateJWT(String sub, DateTime exp, byte[] secretKey,
                Func<dynamic, String> SerializePayloadToJsonFunc,
                Encoding defEncoding = null)
            {
                defEncoding = defEncoding ?? Encoding.UTF8;
    
                // 表头的处理
                String headerBase64Str;
                {
                    var headerJson = "{"alg":"HS256","typ":"JWT"}";
                    headerBase64Str = Base64Encode(defEncoding.GetBytes(headerJson));
                }
    
                // PayLoad的处理
                String payloadBase64Str;
                {
                    var jwtPayLoad = new
                    {
                        // jwt所面向的用户
                        sub = sub,
                        // jwt的签发时间
                        iat = DateTime.Now.ToUTC(),// 1618571381,
                        // jwt的过期时间,这个过期时间必须要大于签发时间
                        exp = exp.ToUTC(),//1618671381,
                    };
    
                    var payLoadJson = SerializePayloadToJsonFunc(jwtPayLoad);//JsonTextHelper.SerializeObject(jwtPayLoad);
                    payloadBase64Str = Base64Encode(defEncoding.GetBytes(payLoadJson));
                }
    
                // Sign的处理
                String signBase64;
                {
                    var sign = $"{headerBase64Str}.{payloadBase64Str}".ToHMACSHA256(secretKey, defEncoding);
                    signBase64 = Base64Encode(sign);
                }
    
                // 最终的jwt字符串
                string jwtStr = $"{headerBase64Str}.{payloadBase64Str}.{signBase64}";
    
                return jwtStr;
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <typeparam name="T">JWTPayLoad的类型</typeparam>
            /// <param name="jwtStr"></param>
            /// <param name="secretKey">密钥</param>
            /// <param name="DeserializePayloadFunc">参数为payloadJson字符串,返回payload对象</param>
            /// <param name="payloadModel">解析的payload数据</param>
            /// <param name="defEncoding">payload的编码类型,用于从jwt字符串中解码出原始payload</param>
            /// <returns></returns>
            public static bool Check<T>(String jwtStr, byte[] secretKey,
                Func<String, T> DeserializePayloadFunc,
                out T payloadModel,
                Encoding defEncoding = null)
                where T : IJWTPayload
            {
                // 校验token是否正确
                bool result;   //True表示通过,False表示未通过
    
                var jwtArr = jwtStr.Split('.');
    
                //2.1. 获取token中的PayLoad中的值,并做过期校验
                var payload = jwtArr[1];
                var payLoadBase64 = Base64Decode(payload);
                var payloadStr = defEncoding.GetString(payLoadBase64);
    
                payloadModel = DeserializePayloadFunc(payloadStr);//JsonConvert.DeserializeObject<T>(payloadStr);  //这一步已经获取到了payload中的值,并进行转换了
                var nowTime = DateTime.Now.ToUTC();
                if (nowTime > payloadModel.exp)
                {
                    //表示token过期,校验未通过
                    result = false;
                    return result;
                }
                else
                {
                    //2.2 做准确性校验
                    var oldSignBase64Str = jwtArr[2];
                    var newSign = $"{jwtArr[0]}.{jwtArr[1]}".ToHMACSHA256(secretKey, defEncoding);
                    var newSignBase64Str = Base64Encode(newSign);
                    return oldSignBase64Str.EqualIgnoreCase(newSignBase64Str);  //true表示检验通过,false表示检验未通过
                }
            }
    
            // Base64编码
            private static string Base64Encode(byte[] data)
            {
                var base64 = Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_').TrimEnd('=');
                return base64;
            }
    
            // Base64解码
            private static byte[] Base64Decode(string base64UrlStr)
            {
                base64UrlStr = base64UrlStr.Replace('-', '+').Replace('_', '/');
                switch (base64UrlStr.Length % 4)
                {
                    case 2:
                        base64UrlStr += "==";
                        break;
                    case 3:
                        base64UrlStr += "=";
                        break;
                }
    
                var bytes = Convert.FromBase64String(base64UrlStr);
                return bytes;
            }
    
            private byte[] Base64UrlDecode3(string base64Str)
            {
                //base64UrlStr = Uri.EscapeDataString(base64UrlStr);
                base64Str = base64Str.Replace('-', '+').Replace('_', '/');
                switch (base64Str.Length % 4)
                {
                    case 2:
                        base64Str += "==";
                        break;
                    case 3:
                        base64Str += "=";
                        break;
                }
                var bytes = Convert.FromBase64String(base64Str);
                return bytes;
            }
    
            /// <summary>
            /// 转换为单字节 java base64
            /// </summary>
            /// <param name="str"></param>
            /// <returns></returns>
            private static string JavaBase64(string str)
            {
                byte[] by = Encoding.UTF8.GetBytes(str);
                sbyte[] sby = new sbyte[by.Length];
                for (int i = 0; i < by.Length; i++)
                {
                    if (by[i] > 127)
                        sby[i] = (sbyte)(by[i] - 256);
                    else
                        sby[i] = (sbyte)by[i];
                }
                byte[] newby = (byte[])(object)sby;
                return Convert.ToBase64String(newby);
            }
    
        }
    
        /// <summary>
        /// 
        /// </summary>
        public interface IJWTPayload
        {
            /// <summary>jwt所面向的用户</summary>
            String sub { get; set; }
            /// <summary>
            /// jwt的签发时间,
            /// UTC 时间,毫秒
            /// </summary>
            long iat { get; set; }
            /// <summary>
            /// jwt的过期时间,这个过期时间必须要大于签发时间,
            /// UTC 时间,毫秒
            /// </summary>
            long exp { get; set; }
        }
    
    }
    

      

      一些扩展方法

            /// <summary>
            /// 
            /// </summary>
            /// <param name="val">待加密的值</param>
            /// <param name="secret">密钥</param>
            /// <param name="defEncoding">待加密的值的编码类型</param>
            /// <returns></returns>
            public static byte[] ToHMACSHA256(this string val, byte[] secret, Encoding defEncoding = null)
            {
                defEncoding = defEncoding ?? Encoding.UTF8;
    
                var messageBytes = defEncoding.GetBytes(val);
                return messageBytes.ToHMACSHA256(secret);
            }
    

      

        public class JWTPayloadModel : IJWTPayload
        {
            public string sub { get; set; }
            public long iat { get; set; }
            public long exp { get; set; }
    
        }
    

     测试Demo

                        // jwt的key 一定是Base64编码过的
                        var keyBase64Str = "QyM2NjY=";// 实际文本为:C#666
                        Console.WriteLine($"创建JWT");
                        var payLoad = "C#、JAVA JWT标准还不一样,坑尼玛的爹啊";
                        var signKeyArr = Convert.FromBase64String(keyBase64Str);
                        var expTime = DateTime.Now.AddYears(1);
    
                        var token = MyJWTTools.CreateJWT(
                            payLoad, expTime,
                            signKeyArr,
                            d => JsonTextHelper.SerializeObject(d),
                            Encoding.UTF8);
                        Console.WriteLine(token);
    
                        Console.WriteLine($"校验JWT");
                        var result = MyJWTTools.Check<JWTPayloadModel>(token, signKeyArr,
                            d => JsonTextHelper.DeserializeObject<JWTPayloadModel>(d),
                            out JWTPayloadModel jWTPayloadModel,
                            Encoding.UTF8);
    
                        Console.WriteLine(result);
                        Console.WriteLine(JsonTextHelper.SerializeObject(jWTPayloadModel));
    

     

    建议线上项目使用成熟的jwt组件

    试了一下,C#的jwt组件需要校验多个payload项,导致校验payload失败,

    可以在生成的方法里 jwtPayLoad 匿名对象中添加指定项

  • 相关阅读:
    Spring Security 4
    JPA查询
    改进的冒泡算法
    Create User
    COM调用 – VB、PB
    J-Link clone问题
    修复山寨版的J-Link
    C# Web版报表
    HTML/CSS/Javascript代码在线压缩、格式化(美化)工具
    eclipse中字母大小写转换快捷键
  • 原文地址:https://www.cnblogs.com/huawublog/p/14673105.html
Copyright © 2011-2022 走看看