解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
9.82 KiB
X
# WebͨÓÃÁîÅÆ JwtBuilder ## ¸ÅÊö `JwtBuilder` ÊÇ NewLife.Core ÖÐµÄ JSON Web Token (JWT) Éú³ÉºÍÑéÖ¤¹¤¾ßÀà¡£JWT ÊÇÒ»ÖÖ½ô´Õ¡¢×Ô°üº¬µÄÁîÅÆ¸ñʽ£¬¹ã·ºÓÃÓÚ Web API ÈÏÖ¤ºÍÊÚȨ¡£JwtBuilder Ö§³Ö HS256/HS384/HS512 ºÍ RS256/RS384/RS512 µÈÖ÷Á÷Ëã·¨¡£ **ÃüÃû¿Õ¼ä**£º`NewLife.Web` **ÎĵµµØÖ·**£ºhttps://newlifex.com/core/jwt ## ºËÐÄÌØÐÔ - **¶àËã·¨Ö§³Ö**£ºHS256¡¢HS384¡¢HS512¡¢RS256¡¢RS384¡¢RS512 - **±ê×¼ÉùÃ÷**£ºÖ§³Ö iss¡¢sub¡¢aud¡¢exp¡¢nbf¡¢iat¡¢jti µÈ±ê×¼ÉùÃ÷ - **×Ô¶¨ÒåÊý¾Ý**£ºÖ§³ÖÔÚÁîÅÆÖÐЯ´øÈÎÒâ×Ô¶¨ÒåÊý¾Ý - **ʱ¼äÑéÖ¤**£º×Ô¶¯ÑéÖ¤¹ýÆÚʱ¼äºÍÉúЧʱ¼ä - **¿ÉÀ©Õ¹**£ºÖ§³Ö×¢²á×Ô¶¨ÒåÇ©ÃûËã·¨ ## ¿ìËÙ¿ªÊ¼ ### Éú³ÉÁîÅÆ ```csharp using NewLife.Web; var builder = new JwtBuilder { Secret = "your-secret-key-at-least-32-characters", Expire = DateTime.Now.AddHours(2), // 2Сʱºó¹ýÆÚ Subject = "user123", // Óû§±êʶ Issuer = "MyApp" // °ä·¢Õß }; // Ìí¼Ó×Ô¶¨ÒåÊý¾Ý builder["role"] = "admin"; builder["name"] = "ÕÅÈý"; // Éú³ÉÁîÅÆ var token = builder.Encode(new { }); Console.WriteLine(token); ``` ### ÑéÖ¤ÁîÅÆ ```csharp var builder = new JwtBuilder { Secret = "your-secret-key-at-least-32-characters" }; if (builder.TryDecode(token, out var message)) { Console.WriteLine($"Óû§: {builder.Subject}"); Console.WriteLine($"½ÇÉ«: {builder["role"]}"); Console.WriteLine($"¹ýÆÚʱ¼ä: {builder.Expire}"); } else { Console.WriteLine($"Ñé֤ʧ°Ü: {message}"); } ``` ## API ²Î¿¼ ### ÊôÐÔ #### ±ê×¼ÉùÃ÷ ```csharp /// <summary>°ä·¢Õß (iss)</summary> public String? Issuer { get; set; } /// <summary>Ö÷ÌåËùÓÐÈË (sub)£¬¿É´æ·ÅÓû§ID</summary> public String? Subject { get; set; } /// <summary>ÊÜÖÚ (aud)</summary> public String? Audience { get; set; } /// <summary>¹ýÆÚʱ¼ä (exp)£¬Ä¬ÈÏ2Сʱ</summary> public DateTime Expire { get; set; } /// <summary>ÉúЧʱ¼ä (nbf)£¬ÔÚ´Ë֮ǰÎÞЧ</summary> public DateTime NotBefore { get; set; } /// <summary>°ä·¢Ê±¼ä (iat)</summary> public DateTime IssuedAt { get; set; } /// <summary>ÁîÅÆ±êʶ (jti)</summary> public String? Id { get; set; } ``` #### ÅäÖÃÊôÐÔ ```csharp /// <summary>Ëã·¨£¬Ä¬ÈÏHS256</summary> public String Algorithm { get; set; } /// <summary>ÁîÅÆÀàÐÍ£¬Ä¬ÈÏJWT</summary> public String? Type { get; set; } /// <summary>ÃÜÔ¿</summary> public String? Secret { get; set; } /// <summary>×Ô¶¨ÒåÊý¾ÝÏî</summary> public IDictionary<String, Object?> Items { get; } ``` #### Ë÷ÒýÆ÷ ```csharp // »ñÈ¡»òÉèÖÃ×Ô¶¨ÒåÊý¾Ý public Object? this[String key] { get; set; } ``` ### Encode - Éú³ÉÁîÅÆ ```csharp public String Encode(Object payload) ``` ½«Êý¾Ý±àÂëΪ JWT ÁîÅÆ×Ö·û´®¡£ **²ÎÊý**£º - `payload`£ºÒª±àÂëµÄÊý¾Ý¶ÔÏó **·µ»ØÖµ**£ºJWT ÁîÅÆ×Ö·û´® **ʾÀý**£º ```csharp var builder = new JwtBuilder { Secret = "my-secret-key-32-characters-long", Expire = DateTime.Now.AddDays(7), Subject = "user_001" }; // ·½Ê½1£ºÊ¹ÓÃÊôÐÔºÍË÷ÒýÆ÷ builder["permissions"] = new[] { "read", "write" }; var token1 = builder.Encode(new { }); // ·½Ê½2£ºÖ±½Ó´«Èë¶ÔÏó var token2 = builder.Encode(new { userId = 123, userName = "test", permissions = new[] { "read", "write" } }); ``` ### TryDecode - ÑéÖ¤²¢½âÂëÁîÅÆ ```csharp public Boolean TryDecode(String token, out String? message) ``` ÑéÖ¤ JWT ÁîÅÆ²¢½âÂëÊý¾Ý¡£ **²ÎÊý**£º - `token`£ºJWT ÁîÅÆ×Ö·û´® - `message`£ºÑé֤ʧ°ÜʱµÄ´íÎóÐÅÏ¢ **·µ»ØÖµ**£ºÑéÖ¤ÊÇ·ñ³É¹¦ **ÑéÖ¤ÄÚÈÝ**£º 1. JWT ¸ñʽÊÇ·ñÕýÈ·£¨Èý¶Îʽ£© 2. Ç©ÃûÊÇ·ñÓÐЧ 3. ÊÇ·ñÔÚÓÐЧÆÚÄÚ 4. ÊÇ·ñÒÑÉúЧ **ʾÀý**£º ```csharp var builder = new JwtBuilder { Secret = "my-secret-key-32-characters-long" }; if (builder.TryDecode(token, out var message)) { // ÑéÖ¤³É¹¦£¬¶ÁÈ¡Êý¾Ý var userId = builder.Subject; var expire = builder.Expire; var permissions = builder["permissions"]; } else { // Ñé֤ʧ°Ü Console.WriteLine($"´íÎó: {message}"); // ¿ÉÄܵĴíÎó: // - "JWT¸ñʽ²»ÕýÈ·" // - "ÁîÅÆÒѹýÆÚ" // - "ÁîÅÆÎ´ÉúЧ" // - "δÉèÖÃÃÜÔ¿" } ``` ### Parse - ½ö½âÎö²»ÑéÖ¤ ```csharp public String[]? Parse(String token) ``` ½ö½âÎöÁîÅÆ½á¹¹£¬²»Ñé֤ǩÃû¡£ÓÃÓÚÐèÒªÔÚÑé֤ǰ¶ÁÈ¡ÄÚÈݵij¡¾°¡£ **·µ»ØÖµ**£ºÈý¶ÎʽÊý×é [header, payload, signature]£¬¸ñʽ´íÎó·µ»Ø null **ʾÀý**£º ```csharp var builder = new JwtBuilder(); var parts = builder.Parse(token); if (parts != null) { // ¿ÉÒÔ¶ÁÈ¡Ëã·¨¡¢¹ýÆÚʱ¼äµÈ Console.WriteLine($"Ëã·¨: {builder.Algorithm}"); Console.WriteLine($"¹ýÆÚ: {builder.Expire}"); // È»ºóÉèÖÃÃÜÔ¿½øÐÐÍêÕûÑéÖ¤ builder.Secret = "..."; if (builder.TryDecode(token, out _)) { } } ``` ### RegisterAlgorithm - ×¢²á×Ô¶¨ÒåËã·¨ ```csharp public static void RegisterAlgorithm( String algorithm, JwtEncodeDelegate encode, JwtDecodeDelegate? decode) ``` ×¢²á×Ô¶¨ÒåÇ©ÃûËã·¨¡£ **ʾÀý**£º ```csharp // ×¢²á×Ô¶¨ÒåËã·¨ JwtBuilder.RegisterAlgorithm( "ES256", (data, secret) => ECDsaHelper.SignSha256(data, secret), (data, secret, signature) => ECDsaHelper.VerifySha256(data, secret, signature) ); // ʹÓÃ×Ô¶¨ÒåËã·¨ var builder = new JwtBuilder { Algorithm = "ES256", Secret = ecdsaPrivateKey }; var token = builder.Encode(new { }); ``` ## Ö§³ÖµÄËã·¨ | Ëã·¨ | ÀàÐÍ | ÃÜÔ¿ÒªÇó | ˵Ã÷ | |------|------|---------|------| | HS256 | HMAC | ¶Ô³ÆÃÜÔ¿ | ĬÈÏËã·¨£¬Êʺϴó¶àÊý³¡¾° | | HS384 | HMAC | ¶Ô³ÆÃÜÔ¿ | ¸ü³¤µÄ¹þÏ£ | | HS512 | HMAC | ¶Ô³ÆÃÜÔ¿ | ×µÄ¹þÏ£ | | RS256 | RSA | ¹«Ë½Ô¿¶Ô | ·Ç¶Ô³Æ¼ÓÃÜ£¬ÊʺϷֲ¼Ê½ | | RS384 | RSA | ¹«Ë½Ô¿¶Ô | ¸ü³¤µÄ¹þÏ£ | | RS512 | RSA | ¹«Ë½Ô¿¶Ô | ×µÄ¹þÏ£ | ## ʹÓó¡¾° ### 1. API ÈÏÖ¤ ```csharp // µÇ¼½Ó¿Ú - Éú³ÉÁîÅÆ [HttpPost("login")] public IActionResult Login(String username, String password) { var user = ValidateUser(username, password); if (user == null) return Unauthorized(); var builder = new JwtBuilder { Secret = Configuration["Jwt:Secret"], Expire = DateTime.Now.AddHours(24), Subject = user.Id.ToString(), Issuer = "MyApi" }; builder["role"] = user.Role; builder["name"] = user.Name; var token = builder.Encode(new { }); return Ok(new { token }); } // ÑéÖ¤Öмä¼þ public class JwtMiddleware { public async Task InvokeAsync(HttpContext context) { var token = context.Request.Headers["Authorization"] .ToString().TrimStart("Bearer "); if (!token.IsNullOrEmpty()) { var builder = new JwtBuilder { Secret = _configuration["Jwt:Secret"] }; if (builder.TryDecode(token, out _)) { context.Items["UserId"] = builder.Subject; context.Items["Role"] = builder["role"]; } } await _next(context); } } ``` ### 2. Ë¢ÐÂÁîÅÆ ```csharp public class TokenService { public (String accessToken, String refreshToken) CreateTokenPair(User user) { // ·ÃÎÊÁîÅÆ - ¶ÌÆÚÓÐЧ var accessBuilder = new JwtBuilder { Secret = _secret, Expire = DateTime.Now.AddMinutes(15), Subject = user.Id.ToString() }; accessBuilder["type"] = "access"; // Ë¢ÐÂÁîÅÆ - ³¤ÆÚÓÐЧ var refreshBuilder = new JwtBuilder { Secret = _secret, Expire = DateTime.Now.AddDays(7), Subject = user.Id.ToString() }; refreshBuilder["type"] = "refresh"; return (accessBuilder.Encode(new { }), refreshBuilder.Encode(new { })); } public String? RefreshAccessToken(String refreshToken) { var builder = new JwtBuilder { Secret = _secret }; if (!builder.TryDecode(refreshToken, out _)) return null; if (builder["type"]?.ToString() != "refresh") return null; // Éú³ÉеķÃÎÊÁîÅÆ var newBuilder = new JwtBuilder { Secret = _secret, Expire = DateTime.Now.AddMinutes(15), Subject = builder.Subject }; newBuilder["type"] = "access"; return newBuilder.Encode(new { }); } } ``` ### 3. RSA ·Ç¶Ô³ÆÇ©Ãû ```csharp // ·þÎñ¶ËÇ©Ãû£¨Ê¹ÓÃ˽Կ£© var privateKey = File.ReadAllText("private.pem"); var builder = new JwtBuilder { Algorithm = "RS256", Secret = privateKey, Expire = DateTime.Now.AddHours(1), Subject = "user123" }; var token = builder.Encode(new { }); // ¿Í»§¶Ë/ÆäËû·þÎñÑéÖ¤£¨Ê¹Óù«Ô¿£© var publicKey = File.ReadAllText("public.pem"); var verifier = new JwtBuilder { Algorithm = "RS256", Secret = publicKey }; if (verifier.TryDecode(token, out var msg)) { Console.WriteLine($"ÑéÖ¤³É¹¦: {verifier.Subject}"); } ``` ## ×î¼Ñʵ¼ù ### 1. °²È«µÄÃÜÔ¿¹ÜÀí ```csharp // ²»ÍƼö£ºÓ²±àÂëÃÜÔ¿ var builder = new JwtBuilder { Secret = "my-secret" }; // ÍÆ¼ö£º´ÓÅäÖûò»·¾³±äÁ¿¶ÁÈ¡ var builder = new JwtBuilder { Secret = Environment.GetEnvironmentVariable("JWT_SECRET") ?? Configuration["Jwt:Secret"] }; ``` ### 2. ºÏÀíµÄ¹ýÆÚʱ¼ä ```csharp // ·ÃÎÊÁîÅÆ£º¶ÌÆÚ£¨15·ÖÖÓ-2Сʱ£© Expire = DateTime.Now.AddMinutes(30); // Ë¢ÐÂÁîÅÆ£ºÖÐÆÚ£¨1-7Ì죩 Expire = DateTime.Now.AddDays(7); // ¼ÇסÎÒÁîÅÆ£º³¤ÆÚ£¨30Ì죩 Expire = DateTime.Now.AddDays(30); ``` ### 3. ×îС»¯ÁîÅÆÄÚÈÝ ```csharp // ²»ÍƼö£º´æ·Å´óÁ¿Êý¾Ý builder["userProfile"] = new { /* ´ó¶ÔÏó */ }; // ÍÆ¼ö£º½ö´æ·Å±ØÒª±êʶ builder.Subject = user.Id.ToString(); // ÐèÒªÏêÇéʱ²éÊý¾Ý¿â builder["role"] = user.Role; // ³£ÓõÄÊÚȨÐÅÏ¢ ``` ### 4. ÑéÖ¤ËùÓÐÉùÃ÷ ```csharp if (builder.TryDecode(token, out var message)) { // ¶îÍâÑéÖ¤°ä·¢Õß if (builder.Issuer != "MyApp") { // °ä·¢Õß²»Æ¥Åä } // ¶îÍâÑéÖ¤ÊÜÖÚ if (builder.Audience != "web-client") { // ÊÜÖÚ²»Æ¥Åä } } ``` ## JWT °²È«×¢ÒâÊÂÏî 1. **²»Òª´æ´¢Ãô¸ÐÊý¾Ý**£ºJWT ĬÈϲ»¼ÓÃÜ£¬payload ¿É±» Base64 ½âÂë 2. **ʹÓà HTTPS**£º·ÀÖ¹ÁîÅÆ±»ÖмäÈ˽ػñ 3. **ÉèÖúÏÀí¹ýÆÚʱ¼ä**£º½µµÍÁîÅÆ±»µÁÓõķçÏÕ 4. **ʹÓÃ×ã¹»³¤µÄÃÜÔ¿**£ºHS256 ÖÁÉÙ 32 ×Ö½Ú 5. **ÑéÖ¤ËùÓÐÉùÃ÷**£º°üÀ¨ iss¡¢aud¡¢exp µÈ ## Ïà¹ØÁ´½Ó - [·Ö²¼Ê½Êý×ÖÇ©ÃûÁîÅÆ TokenProvider](/NewLife/X/Blob/dev/Doc/token_provider-·Ö²¼Ê½Êý×ÖÇ©ÃûÁîÅÆTokenProvider.md) - [°²È«À©Õ¹ SecurityHelper](/NewLife/X/Blob/dev/Doc/security_helper-°²È«À©Õ¹SecurityHelper.md)