新增数据同步指南和令牌使用指南,更新 README.md 文档;新增 CLSDataService 结构体及相关方法,支持 SQLite 数据库的备份和恢复功能;新增 token 包及其单元测试,提供 JWT 令牌的校验与集成支持。
This commit is contained in:
26
README.md
26
README.md
@ -11,8 +11,30 @@
|
||||
import "git.mazhangjing.com/corkine/cls-client/cls"
|
||||
```
|
||||
|
||||
CloudLiteSync 支持基于 JWT(JSON 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 支持基于 JWT(JSON 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
80
data/data.go
Normal 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
|
||||
}
|
@ -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 {
|
@ -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)
|
Reference in New Issue
Block a user