解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
|
# 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)
|