|
||
---|---|---|
.. | ||
domain | ||
mock/repository | ||
repository | ||
usecase | ||
README.md |
README.md
Permission Module
JWT Token 和 Refresh Token 管理模組,提供完整的身份驗證和授權功能。
📋 功能特性
🔐 JWT Token 管理
- Access Token 生成: 基於 JWT 標準生成存取權杖
- Refresh Token 機制: 支援長期有效的刷新權杖
- One-Time Token: 臨時性權杖,用於特殊場景
- Token 驗證: 完整的權杖驗證和解析功能
🚫 黑名單機制
- 即時撤銷: 將 JWT 權杖立即加入黑名單
- 用戶登出: 支援單一設備或全設備登出
- 自動過期: 黑名單條目會在權杖過期後自動清理
- 批量管理: 支援批量黑名單操作
💾 Redis 儲存
- 高效能: 使用 Redis 作為主要儲存引擎
- TTL 管理: 自動管理權杖過期時間
- 關聯管理: 支援用戶、設備與權杖的關聯查詢
🔒 安全特性
- HMAC-SHA256: 使用安全的簽名算法
- 密鑰分離: Access Token 和 Refresh Token 使用不同密鑰
- 設備限制: 支援每用戶、每設備的權杖數量限制
- 過期控制: 靈活的權杖過期時間配置
🏗️ 架構設計
本模組遵循 Clean Architecture 原則:
pkg/permission/
├── domain/ # 領域層
│ ├── entity/ # 實體定義
│ ├── repository/ # 儲存庫介面
│ ├── usecase/ # 用例介面
│ └── token/ # 權杖相關常數和類型
├── usecase/ # 用例實現
├── repository/ # 儲存庫實現
└── mock/ # 測試模擬
領域層 (Domain)
- Entity: 定義核心業務實體(Token、BlacklistEntry、Ticket)
- Repository Interface: 定義資料存取介面
- UseCase Interface: 定義業務用例介面
- Token Types: 權杖類型和常數定義
用例層 (UseCase)
- TokenUseCase: 核心業務邏輯實現
- JWT 處理: 權杖生成、解析、驗證
- 黑名單管理: 權杖撤銷和黑名單查詢
儲存層 (Repository)
- Redis 實現: 基於 Redis 的資料存取
- 關聯管理: 用戶、設備、權杖關聯
- TTL 管理: 自動過期處理
🚀 快速開始
1. 配置設定
在 internal/config/config.go
中添加 Token 配置:
type Config struct {
// ... 其他配置
Token struct {
AccessSecret string // Access Token 簽名密鑰
RefreshSecret string // Refresh Token 簽名密鑰
AccessTokenExpiry time.Duration // Access Token 過期時間
RefreshTokenExpiry time.Duration // Refresh Token 過期時間
OneTimeTokenExpiry time.Duration // 一次性 Token 過期時間
MaxTokensPerUser int // 每用戶最大 Token 數
MaxTokensPerDevice int // 每設備最大 Token 數
}
}
2. 初始化模組
import (
"backend/pkg/permission/repository"
"backend/pkg/permission/usecase"
)
// 初始化 Repository
tokenRepo := repository.MustTokenRepository(repository.TokenRepositoryParam{
Redis: redisClient,
})
// 初始化 UseCase
tokenUseCase := usecase.MustTokenUseCase(usecase.TokenUseCaseParam{
TokenRepo: tokenRepo,
Config: config,
})
3. 基本使用
創建 Access Token
req := entity.AuthorizationReq{
GrantType: token.PasswordCredentials.ToString(),
Scope: "read write",
DeviceID: "device123",
IsRefreshToken: true,
Claims: map[string]string{
"uid": "user123",
"role": "admin",
},
}
resp, err := tokenUseCase.NewToken(ctx, req)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Access Token: %s\n", resp.AccessToken)
fmt.Printf("Refresh Token: %s\n", resp.RefreshToken)
驗證 Token
req := entity.ValidationTokenReq{
Token: accessToken,
}
resp, err := tokenUseCase.ValidationToken(ctx, req)
if err != nil {
log.Printf("Token validation failed: %v", err)
return
}
fmt.Printf("Token is valid for user: %s\n", resp.Token.UID)
撤銷 Token (加入黑名單)
err := tokenUseCase.BlacklistToken(ctx, accessToken, "user logout")
if err != nil {
log.Printf("Failed to blacklist token: %v", err)
}
檢查黑名單
isBlacklisted, err := tokenUseCase.IsTokenBlacklisted(ctx, jti)
if err != nil {
log.Printf("Failed to check blacklist: %v", err)
}
if isBlacklisted {
log.Println("Token is blacklisted")
}
🧪 測試
運行測試
# 運行所有測試
go test ./pkg/permission/...
# 運行特定模組測試
go test ./pkg/permission/usecase/
go test ./pkg/permission/repository/
# 運行測試並顯示覆蓋率
go test -cover ./pkg/permission/...
# 生成覆蓋率報告
go test -coverprofile=coverage.out ./pkg/permission/...
go tool cover -html=coverage.out
測試結構
- UseCase Tests: 業務邏輯測試,使用 Mock Repository
- Repository Tests: 資料存取測試,使用 MiniRedis
- JWT Tests: 權杖生成和解析測試
- Integration Tests: 整合測試
📊 API 參考
TokenUseCase 介面
type TokenUseCase interface {
// 基本 Token 操作
NewToken(ctx context.Context, req entity.AuthorizationReq) (entity.TokenResp, error)
RefreshToken(ctx context.Context, req entity.RefreshTokenReq) (entity.RefreshTokenResp, error)
ValidationToken(ctx context.Context, req entity.ValidationTokenReq) (entity.ValidationTokenResp, error)
// Token 管理
CancelToken(ctx context.Context, req entity.CancelTokenReq) error
CancelTokens(ctx context.Context, req entity.DoTokenByUIDReq) error
CancelTokenByDeviceID(ctx context.Context, req entity.DoTokenByDeviceIDReq) error
// 查詢操作
GetUserTokensByUID(ctx context.Context, req entity.QueryTokenByUIDReq) ([]*entity.TokenResp, error)
GetUserTokensByDeviceID(ctx context.Context, req entity.DoTokenByDeviceIDReq) ([]*entity.TokenResp, error)
// 一次性 Token
NewOneTimeToken(ctx context.Context, req entity.CreateOneTimeTokenReq) (entity.CreateOneTimeTokenResp, error)
CancelOneTimeToken(ctx context.Context, req entity.CancelOneTimeTokenReq) error
// 黑名單操作
BlacklistToken(ctx context.Context, token string, reason string) error
IsTokenBlacklisted(ctx context.Context, jti string) (bool, error)
BlacklistAllUserTokens(ctx context.Context, uid string, reason string) error
// 工具方法
ReadTokenBasicData(ctx context.Context, token string) (map[string]string, error)
}
主要實體
Token 實體
type Token struct {
ID string // 權杖唯一標識
UID string // 用戶 ID
DeviceID string // 設備 ID
AccessToken string // Access Token
RefreshToken string // Refresh Token
ExpiresIn int // 過期時間(秒)
AccessCreateAt time.Time // Access Token 創建時間
RefreshCreateAt time.Time // Refresh Token 創建時間
RefreshExpiresIn int // Refresh Token 過期時間(秒)
}
黑名單實體
type BlacklistEntry struct {
JTI string // JWT ID
UID string // 用戶 ID
TokenID string // Token ID
Reason string // 加入黑名單原因
ExpiresAt int64 // 原始權杖過期時間
CreatedAt int64 // 加入黑名單時間
}
🔧 配置參數
參數 | 類型 | 說明 | 預設值 |
---|---|---|---|
AccessSecret |
string | Access Token 簽名密鑰 | 必填 |
RefreshSecret |
string | Refresh Token 簽名密鑰 | 必填 |
AccessTokenExpiry |
Duration | Access Token 過期時間 | 15分鐘 |
RefreshTokenExpiry |
Duration | Refresh Token 過期時間 | 7天 |
OneTimeTokenExpiry |
Duration | 一次性 Token 過期時間 | 5分鐘 |
MaxTokensPerUser |
int | 每用戶最大 Token 數 | 10 |
MaxTokensPerDevice |
int | 每設備最大 Token 數 | 5 |
🚨 錯誤處理
模組定義了完整的錯誤類型:
// Token 驗證錯誤
var (
ErrInvalidTokenID = errors.New("invalid token ID")
ErrInvalidUID = errors.New("invalid UID")
ErrTokenExpired = errors.New("token expired")
ErrTokenNotFound = errors.New("token not found")
)
// JWT 特定錯誤
var (
ErrInvalidJWTToken = errors.New("invalid JWT token")
ErrJWTSigningFailed = errors.New("JWT signing failed")
ErrJWTParsingFailed = errors.New("JWT parsing failed")
)
// 黑名單錯誤
var (
ErrTokenBlacklisted = errors.New("token is blacklisted")
ErrBlacklistNotFound = errors.New("blacklist entry not found")
)
🔒 安全考量
1. 密鑰管理
- 使用強密鑰(至少 256 位)
- Access Token 和 Refresh Token 使用不同密鑰
- 定期輪換密鑰
2. 權杖過期
- Access Token 使用較短過期時間(15分鐘)
- Refresh Token 使用較長過期時間(7天)
- 支援自定義過期時間
3. 黑名單機制
- 即時撤銷可疑權杖
- 支援批量撤銷
- 自動清理過期條目
4. 限制機制
- 每用戶權杖數量限制
- 每設備權杖數量限制
- 防止權杖濫用
📈 效能優化
1. Redis 優化
- 使用適當的 TTL 避免記憶體洩漏
- 批量操作減少網路往返
- 使用 Pipeline 提升效能
2. JWT 優化
- 最小化 Claims 數據大小
- 使用高效的序列化格式
- 快取常用的解析結果
3. 黑名單優化
- 使用 SCAN 而非 KEYS 遍歷
- 批量檢查黑名單狀態
- 定期清理過期條目
🤝 貢獻指南
- Fork 本專案
- 創建功能分支 (
git checkout -b feature/amazing-feature
) - 提交變更 (
git commit -m 'Add some amazing feature'
) - 推送到分支 (
git push origin feature/amazing-feature
) - 開啟 Pull Request
開發規範
- 遵循 Go 編碼規範
- 保持測試覆蓋率 > 80%
- 添加適當的文檔註釋
- 使用有意義的提交訊息
📄 授權條款
本專案採用 MIT 授權條款 - 詳見 LICENSE 檔案
📞 聯絡資訊
如有問題或建議,請通過以下方式聯絡:
- 開啟 Issue
- 發送 Pull Request
- 聯絡維護團隊
注意: 本模組是 PlayOne Backend 專案的一部分,請確保與整體架構保持一致。