v9.10.2019.0101 全面巩固批量Insert/Update/Upsert,支持数据备份、恢复和同步,支持实体列表保存到文件以及加载
|
# 分布å¼ä»¤ç‰ŒTokenProvider
## 概述
`TokenProvider` 是 NewLife.Core æä¾›çš„è½»é‡çº§åˆ†å¸ƒå¼èº«ä»½ä»¤ç‰Œæ–¹æ¡ˆï¼Œé‡‡ç”¨ **DSA éžå¯¹ç§°ç¾å**(由 `DSAHelper` å°è£…ï¼‰ï¼Œæ— éœ€å…±äº«å¯†é’¥å³å¯åœ¨å¤šèŠ‚ç‚¹é—´éªŒè¯ä»¤ç‰Œã€‚ç¾åèŠ‚ç‚¹æŒæœ‰ç§é’¥ï¼ˆ`.prvkey`),验è¯èŠ‚ç‚¹åªéœ€æŒæœ‰å¯¹åº”公钥(`.pubkey`),适åˆå¾®æœåŠ¡å†…éƒ¨ API 鉴æƒå’Œè®¾å¤‡æ³¨å†Œä»¤ç‰Œåœºæ™¯ã€‚
**命å空间**:`NewLife.Security`
**文档地å€**:/core/token_provider
## æ ¸å¿ƒç‰¹æ€§
- **DSA éžå¯¹ç§°ç¾å**:ç¾å‘æ–¹æŒæœ‰ç§é’¥ï¼ŒéªŒè¯æ–¹ä»…需公钥,é™ä½Žå¯†é’¥æ³„露风险
- **自动生æˆå¯†é’¥å¯¹**:首次调用 `ReadKey()` 时自动生æˆå¹¶ä¿å˜ `.prvkey` / `.pubkey` 文件
- **零外部ä¾èµ–**:基于 `DSAHelper`(NewLife å†…ç½®ï¼‰ï¼Œæ— éœ€ç¬¬ä¸‰æ–¹ JWT 库
- **è½»é‡è½½è·**:令牌内å«ç”¨æˆ·æ ‡è¯†å’Œè¿‡æœŸæ—¶é—´ï¼Œæ— é¢å¤– claims,体积æžå°
- **多节点共享公钥**:仅需分å‘公钥文件到å„验è¯èŠ‚ç‚¹ï¼Œå³å¯å®Œæˆåˆ†å¸ƒå¼éªŒè¯
## 快速开始
### ç¾å‘令牌(æœåŠ¡ç«¯ï¼‰
```csharp
using NewLife.Security;
// æœåŠ¡ç«¯ä½¿ç”¨ç§é’¥ç¾å‘
var provider = new TokenProvider();
provider.ReadKey("token.prvkey", true); // true = ç§é’¥æ¨¡å¼
// ç¾å‘有效期 24 å°æ—¶çš„令牌
var token = provider.Encode("user001", TimeSpan.FromHours(24));
Console.WriteLine($"令牌: {token}");
```
### 验è¯ä»¤ç‰Œï¼ˆéªŒè¯èŠ‚ç‚¹ï¼‰
```csharp
// 验è¯èŠ‚ç‚¹åªéœ€å…¬é’¥
var verifier = new TokenProvider();
verifier.ReadKey("token.pubkey", false); // false = 公钥模å¼
if (verifier.TryDecode(token, out var user, out var expire))
{
Console.WriteLine($"用户: {user},过期: {expire:yyyy-MM-dd HH:mm:ss}");
}
else
{
Console.WriteLine("ä»¤ç‰Œæ— æ•ˆæˆ–å·²è¿‡æœŸ");
}
```
## API å‚考
### 属性
```csharp
/// <summary>密钥内容(DERæ ¼å¼Base64ç¼–ç )。ç§é’¥æˆ–公钥,å–å†³äºŽè¯»å–æ–¹å¼</summary>
public String? Key { get; set; }
```
### 方法
#### ReadKey - è¯»å–æˆ–生æˆå¯†é’¥
```csharp
/// <summary>读å–密钥文件,文件ä¸å˜åœ¨æ—¶è‡ªåŠ¨ç”Ÿæˆå¯†é’¥å¯¹</summary>
/// <param name="keyFile">密钥文件路径(.prvkey 或 .pubkey)</param>
/// <param name="isPrivate">true=ç§é’¥æ¨¡å¼ï¼ˆç¾å‘),false=公钥模å¼ï¼ˆéªŒè¯ï¼‰</param>
public void ReadKey(String keyFile, Boolean isPrivate)
```
**行为**:
1. è‹¥ `keyFile` å˜åœ¨ï¼Œç›´æŽ¥è¯»å–写入 `Key`
2. è‹¥ä¸å˜åœ¨ä¸” `isPrivate=true`,调用 `DSAHelper.GenerateKey()` 生æˆå¯†é’¥å¯¹ï¼Œå°†ç§é’¥å†™å…¥ `keyFile`,将对应公钥写入 `keyFile` è¿½åŠ `.pubkey` (å³åŒç›®å½•ä¸‹ç”Ÿæˆ `xxx.pubkey`)
3. è‹¥ä¸å˜åœ¨ä¸” `isPrivate=false`,å°è¯•从 `keyFile.Replace(".pubkey","").prvkey` 䏿å–公钥;ä»ä¸å˜åœ¨åˆ™æ£å¸¸æŠ¥é”™
#### Encode - ç¾å‘令牌
```csharp
/// <summary>ç¾å‘令牌</summary>
/// <param name="user">ç”¨æˆ·æ ‡è¯†ï¼ˆä¸šåŠ¡ ID 或用户å)</param>
/// <param name="expire">有效期</param>
/// <returns>令牌å—ç¬¦ä¸²ï¼ˆæ ¼å¼ï¼šbase64url(data).base64url(signature))</returns>
public String Encode(String user, TimeSpan expire)
```
ä»¤ç‰Œæ ¼å¼ï¼š
```
base64url(user + "," + unixTimestamp) . base64url(DSAç¾å)
```
å…¶ä¸ `unixTimestamp` 是过期ç»å¯¹æ—¶é—´ï¼ˆç§’级 Unix 时间戳)。
#### TryDecode - 验è¯ä»¤ç‰Œ
```csharp
/// <summary>验è¯ä»¤ç‰Œå¹¶æå–è´Ÿè½½</summary>
/// <param name="token">令牌å—符串</param>
/// <param name="user">è¾“å‡ºï¼šç”¨æˆ·æ ‡è¯†</param>
/// <param name="expire">输出:过期时间</param>
/// <returns>true=令牌有效且未过期</returns>
public Boolean TryDecode(String token, out String? user, out DateTime expire)
```
**éªŒè¯æ¥éª¤**:
1. 按 `.` åˆ†å‰²ä»¤ç‰Œï¼Œå–æ•°æ®æ®µå’Œç¾å段
2. Base64url è§£ç æ•°æ®æ®µå’Œç¾å段
3. ä½¿ç”¨å…¬é’¥éªŒè¯ DSA ç¾å
4. è§£æž `user,unixTimestamp`,检查过期时间
## 部署模å¼
### 啿œºæ¨¡å¼ï¼ˆç§é’¥æœ¬åœ°ç¾å‘ + 验è¯ï¼‰
```
应用进程
Tokenç¾å‘(private key)
Token验è¯ï¼ˆpublic key)
文件:token.prvkey + token.pubkey(åŒä¸€ç›®å½•)
```
### 网关集ä¸é‰´æƒæ¨¡å¼
```
[ç¾å‘æœåŠ¡] æŒæœ‰ token.prvkey
ç¾å‘令牌
[客户端App] æºå¸¦ä»¤ç‰Œ [å¾®æœåŠ¡A, B, C, D]
凿Œæœ‰ token.pubkey
本地验è¯ï¼Œæ— 需回调ç¾å‘æœåŠ¡
```
### 多环境密钥隔离
```csharp
// 按环境使用ä¸åŒå¯†é’¥æ–‡ä»¶
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "production";
var provider = new TokenProvider();
provider.ReadKey($"token-{env}.prvkey", true);
```
## 使用场景
### 场景一:微æœåŠ¡å†…éƒ¨è°ƒç”¨é‰´æƒ
```csharp
// gateway å¯åŠ¨æ—¶åˆå§‹åŒ–ç¾å‘者
var issuer = new TokenProvider();
issuer.ReadKey("gateway.prvkey", true);
// 登录时ç¾å‘
app.MapPost("/login", (LoginRequest req) =>
{
if (!AuthUser(req)) return Results.Unauthorized();
var token = issuer.Encode(req.Username, TimeSpan.FromHours(8));
return Results.Ok(new { token });
});
// 下游æœåŠ¡éªŒè¯ï¼ˆåªéœ€å…¬é’¥ï¼‰
var verifier = new TokenProvider();
verifier.ReadKey("gateway.pubkey", false);
app.Use(async (ctx, next) =>
{
var token = ctx.Request.Headers["X-Token"];
if (!verifier.TryDecode(token, out var user, out _))
{
ctx.Response.StatusCode = 401;
return;
}
ctx.Items["user"] = user;
await next();
});
```
### 场景二:IoT 设备激活ç
```csharp
// 控制å°å·¥å…·ç”Ÿæˆè®¾å¤‡æ¿€æ´»ç (ç§é’¥åœ¨å†…部)
var provider = new TokenProvider();
provider.ReadKey("activation.prvkey", true);
var deviceId = "DEV-20250701-001";
var code = provider.Encode(deviceId, TimeSpan.FromDays(365));
// 设备端验è¯ï¼ˆå…¬é’¥å†…嵌在固件ä¸ï¼‰
var verifier = new TokenProvider { Key = EMBEDDED_PUBLIC_KEY };
if (verifier.TryDecode(code, out var id, out var expire) && id == deviceId)
Console.WriteLine("激活æˆåŠŸ");
```
## 注æ„事项
- **ç§é’¥ä¸¥æ ¼ä¿æŠ¤**:`.prvkey` 文件ä¸è¦æäº¤åˆ°ç‰ˆæœ¬æŽ§åˆ¶ï¼Œåº”通过密钥管ç†ç³»ç»Ÿï¼ˆVault/KMS)分å‘。
- **令牌ä¸å¯æ’¤é”€**ï¼šä»¤ç‰Œå†…æ— ä¼šè¯ IDï¼Œæ— æ³•åœ¨è¿‡æœŸå‰ä¸»åЍåŠé”€ï¼Œéœ€é€šè¿‡çŸæœ‰æ•ˆæœŸ + 刷新令牌缓解。
- **ä¸å«è§’色信æ¯**:载è·ä»…å« `user` 和过期时间,æƒé™æ£€æŸ¥éœ€åœ¨ä¸šåŠ¡å±‚è‡ªè¡Œå®žçŽ°ã€‚
- **仅适åˆå†…部æœåŠ¡**:对外 API å»ºè®®ä½¿ç”¨æ ‡å‡† JWT(OIDC),`TokenProvider` é¢å‘内部高性能场景。
- **æ—¶é’Ÿåå·®**:ç¾å‘å’ŒéªŒè¯æ–¹æœåŠ¡å™¨æ—¶é’Ÿåº”ä¿æŒåœ¨ 30 秒内,å¯åœ¨æœ‰æ•ˆæœŸå†…预留缓冲。
|