新增数据同步指南和令牌使用指南,更新 README.md 文档;新增 CLSDataService 结构体及相关方法,支持 SQLite 数据库的备份和恢复功能;新增 token 包及其单元测试,提供 JWT 令牌的校验与集成支持。

This commit is contained in:
2025-07-22 21:18:07 +08:00
parent abd72a10b1
commit 29071aa83f
4 changed files with 117 additions and 15 deletions

View File

@ -11,8 +11,30 @@
import "git.mazhangjing.com/corkine/cls-client/cls"
```
CloudLiteSync 支持基于 JWTJSON Web Token的令牌鉴权机制适用于微服务、Serverless、API 网关等多种场景。每个令牌项目拥有独立的 RSA 公私钥对,支持灵活的权限与用途隔离。
## 数据同步指南
CloudLiteSync 支持 SQLite 数据库备份和初始化环境自动恢复。
```go
package main
import (
"time"
"git.mazhangjing.com/corkine/cls-client/token"
)
func main() {
service := token.NewCLSDataService(
"https://xxx.yyy.com/api/appName",
"TQ2A71GSF3DNJ1I5SL4HVREOEQYLIO44",
"data/app.db")
err := service.DownloadLatestDB()
err = service.UploadDB("Backup at" + time.Now().Format("2006-01-02 15:04:05"))
}
```
## 令牌使用指南
CloudLiteSync 支持基于 JWTJSON Web Token的令牌鉴权机制。每个令牌项目拥有独立的 RSA 公私钥对,支持灵活的权限与用途隔离。
```go
package main
@ -24,7 +46,7 @@ import (
func main() {
// 假设 publicKeyPEM、matchPurpose、remoteServer 已获取
service := cls.NewCLSService(publicKeyPEM, matchPurpose, remoteServer)
service := cls.NewCLSAuthService(publicKeyPEM, matchPurpose, remoteServer)
// 1. 使用 JWT Token 进行本地校验
claims, err := service.JwtAuth(tokenString)

80
data/data.go Normal file
View File

@ -0,0 +1,80 @@
package data
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
type CLSDataService struct {
ServerURL string // https://xxx/api/your_project
Token string // auth token
DBPath string // upload and download db path
}
func NewCLSDataService(serverURL, token, dbPath string) *CLSDataService {
return &CLSDataService{
ServerURL: serverURL,
Token: token,
DBPath: dbPath,
}
}
// 下载最新版本数据库
func (s *CLSDataService) DownloadLatestDB() error {
return s.DownloadDB("latest")
}
// 下载指定版本数据库SQLite MD5 Hash)
func (s *CLSDataService) DownloadDB(fileHash string) error {
resp, err := http.Get(fmt.Sprintf("%s/%s?token=%s", s.ServerURL, fileHash, s.Token))
if err != nil {
return err
}
defer resp.Body.Close()
out, err := os.Create(s.DBPath)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
return err
}
// 上传数据库
func (s *CLSDataService) UploadDB(description string) error {
file, err := os.Open(s.DBPath)
if err != nil {
return err
}
defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
writer.WriteField("token", s.Token)
writer.WriteField("description", description)
part, err := writer.CreateFormFile("database", "data.db")
if err != nil {
return err
}
io.Copy(part, file)
writer.Close()
req, err := http.NewRequest("POST", s.ServerURL, body)
if err != nil {
return err
}
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@ -1,5 +1,5 @@
// Package cls 提供 CloudLiteSync JWT 令牌校验与集成支持。
package cls
package token
import (
"crypto/rsa"
@ -14,8 +14,8 @@ import (
"github.com/golang-jwt/jwt/v5"
)
// CLSService 提供 JWT 校验与远程令牌获取服务。
type CLSService struct {
// CLSAuthService 提供 JWT 校验与远程令牌获取服务。
type CLSAuthService struct {
// PublicKey 用于校验 JWT 的 RSA 公钥
PublicKey *rsa.PublicKey
// MatchPurpose 期望的 purpose 字段,用于用途隔离
@ -24,9 +24,9 @@ type CLSService struct {
RemoteServer string
}
// NewCLSService 创建一个新的 CLSService 实例。
// NewCLSTokenService 创建一个新的 CLSService 实例。
// publicKeyPEM 为 PEM 格式的 RSA 公钥matchPurpose 为用途标识remoteServer 为远程服务地址。
func NewCLSService(publicKeyPEM, matchPurpose, remoteServer string) *CLSService {
func NewCLSTokenService(publicKeyPEM, matchPurpose, remoteServer string) *CLSAuthService {
block, _ := pem.Decode([]byte(publicKeyPEM))
if block == nil {
panic("failed to parse PEM block")
@ -35,7 +35,7 @@ func NewCLSService(publicKeyPEM, matchPurpose, remoteServer string) *CLSService
if err != nil {
panic(err)
}
return &CLSService{
return &CLSAuthService{
PublicKey: pub.(*rsa.PublicKey),
MatchPurpose: matchPurpose,
RemoteServer: remoteServer,
@ -53,7 +53,7 @@ type JWTClaims struct {
// JwtAuth 使用 JWT Token 进行本地校验,返回用户声明信息。
// tokenString 为待校验的 JWT 字符串。
// 校验 purpose 字段与服务配置是否一致,并检查过期时间。
func (s *CLSService) JwtAuth(tokenString string) (*JWTClaims, error) {
func (s *CLSAuthService) JwtAuth(tokenString string) (*JWTClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
// 校验签名算法是否为 RSA
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
@ -81,7 +81,7 @@ func (s *CLSService) JwtAuth(tokenString string) (*JWTClaims, error) {
// TokenAuth 通过 quickKey 从远程服务获取并校验 JWT Token返回用户声明信息。
// key 为远程服务分发的 quickKey。
func (s *CLSService) TokenAuth(key string) (*JWTClaims, error) {
func (s *CLSAuthService) TokenAuth(key string) (*JWTClaims, error) {
url := fmt.Sprintf("%s/s/%s", s.RemoteServer, key)
resp, err := http.Get(url)
if err != nil {

View File

@ -1,4 +1,4 @@
package cls
package token
import (
"testing"
@ -16,7 +16,7 @@ JQIDAQAB
-----END PUBLIC KEY-----`
func TestTokenAuth_Mock(t *testing.T) {
cls := NewCLSService(testPublicKey, "MiniCatch", "https://cls.mazhangjing.com")
cls := NewCLSTokenService(testPublicKey, "MiniCatch", "https://cls.mazhangjing.com")
claims, err := cls.JwtAuth("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNvcmtpbmUiLCJyb2xlIjoiYWRtaW4iLCJwdXJwb3NlIjoiTWluaUNhdGNoIiwiZXhwIjoxNzUzOTczNzYwLCJuYmYiOjE3NTMxNjczNjcsImlhdCI6MTc1MzE2NzM2N30.n_sWBUPkTjBa8Uuu_gFd5rw7mOvo3ccINuymFamserBos9WPxiOT9FEf9sPvT-bA7GAQ9K5RFdvWhAmDXRHryraxtk1nzf-WQeV6REjVdwbqHZxBQT0e8DnvPLpLkAbFtK-KP_CAlX3bEj-l7Mq8MzsREKJ456qJIIeybIxCwpmyJ--s2iRwcRS-5Y5kTrXMZqxkiGsLjjlLwKglgG7sqBcZNvPiNCkhnr3Jlj6H74_xF3jAdbBflrns_D4SCm-3IhaPvqpKEEnq6Un_oD1ZbvSsHxZozQKYIR4lqmmBvFo0BKyWOVL5yL8nrooF8Cj1SKMihisL_DgqFoyU65lCIQ")
if err != nil {
t.Fatalf("JwtAuth failed: %v", err)
@ -25,7 +25,7 @@ func TestTokenAuth_Mock(t *testing.T) {
}
func TestTokenAuthFailed(t *testing.T) {
cls := NewCLSService(testPublicKey, "MiniCatch2", "https://cls.mazhangjing.com")
cls := NewCLSTokenService(testPublicKey, "MiniCatch2", "https://cls.mazhangjing.com")
claims, err := cls.JwtAuth("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNvcmtpbmUiLCJyb2xlIjoiYWRtaW4iLCJwdXJwb3NlIjoiTWluaUNhdGNoIiwiZXhwIjoxNzUzOTczNzYwLCJuYmYiOjE3NTMxNjczNjcsImlhdCI6MTc1MzE2NzM2N30.n_sWBUPkTjBa8Uuu_gFd5rw7mOvo3ccINuymFamserBos9WPxiOT9FEf9sPvT-bA7GAQ9K5RFdvWhAmDXRHryraxtk1nzf-WQeV6REjVdwbqHZxBQT0e8DnvPLpLkAbFtK-KP_CAlX3bEj-l7Mq8MzsREKJ456qJIIeybIxCwpmyJ--s2iRwcRS-5Y5kTrXMZqxkiGsLjjlLwKglgG7sqBcZNvPiNCkhnr3Jlj6H74_xF3jAdbBflrns_D4SCm-3IhaPvqpKEEnq6Un_oD1ZbvSsHxZozQKYIR4lqmmBvFo0BKyWOVL5yL8nrooF8Cj1SKMihisL_DgqFoyU65lCIQ")
if err != nil {
t.Logf("JwtAuth failed: %v", err)
@ -35,7 +35,7 @@ func TestTokenAuthFailed(t *testing.T) {
}
func TestTokenTotpAuth1(t *testing.T) {
cls := NewCLSService(testPublicKey, "MiniCatch", "https://cls.mazhangjing.com")
cls := NewCLSTokenService(testPublicKey, "MiniCatch", "https://cls.mazhangjing.com")
claims, err := cls.TokenAuth("638963")
if err != nil {
t.Logf("TokenAuth failed: %v", err)
@ -45,7 +45,7 @@ func TestTokenTotpAuth1(t *testing.T) {
}
func TestTokenTotpAuth2(t *testing.T) {
cls := NewCLSService(testPublicKey, "MiniCatch2", "https://cls.mazhangjing.com")
cls := NewCLSTokenService(testPublicKey, "MiniCatch2", "https://cls.mazhangjing.com")
claims, err := cls.TokenAuth("638963")
if err != nil {
t.Logf("TokenAuth failed: %v", err)