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 匿名对象中添加指定项