From 3590fa7d5852aa8891fe1ce41c238d4edecfbb2e Mon Sep 17 00:00:00 2001 From: zaunist Date: Sun, 6 Apr 2025 04:56:29 +0800 Subject: [PATCH] feat: use worker versionId as jwt_secret --- .github/workflows/build.yml | 44 ++++++++- agent/README.md | 4 - agent/cmd/agent/root.go | 1 - agent/cmd/agent/start.go | 18 +--- agent/pkg/reporter/reporter.go | 58 +++++------ backend/src/index.ts | 40 -------- backend/src/models/db.ts | 2 - backend/src/routes/agents.ts | 128 +++++------------------- backend/src/routes/auth.ts | 1 - backend/src/routes/status.ts | 20 +--- backend/src/utils/jwt.ts | 172 +++++++++++++++++++++++++++++++-- backend/wrangler.toml | 3 + 12 files changed, 269 insertions(+), 222 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8b02949..c632c6e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,12 +42,35 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: 0 # 获取完整的 git 历史用于获取提交信息 - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.24' + - name: Get version info + id: get_version + shell: bash + run: | + # 从 GitHub 环境获取版本信息 + if [[ $GITHUB_REF == refs/tags/* ]]; then + VERSION=${GITHUB_REF#refs/tags/} + else + VERSION="dev-$(git rev-parse --short HEAD)" + fi + COMMIT_HASH=$(git rev-parse --short HEAD) + BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "COMMIT_HASH=$COMMIT_HASH" >> $GITHUB_ENV + echo "BUILD_DATE=$BUILD_DATE" >> $GITHUB_ENV + + echo "版本: $VERSION" + echo "提交哈希: $COMMIT_HASH" + echo "构建日期: $BUILD_DATE" + - name: Build Linux if: matrix.os == 'ubuntu-latest' env: @@ -55,7 +78,12 @@ jobs: GOARCH: ${{ matrix.arch }} run: | cd agent - go build -v -ldflags="-s -w -extldflags=-static" -tags netgo,osusergo -o ${{ matrix.artifact_name }} . + go build -v \ + -ldflags="-s -w -extldflags=-static \ + -X github.com/xugou/agent/cmd/agent.Version=${{ env.VERSION }} \ + -X github.com/xugou/agent/cmd/agent.GitCommit=${{ env.COMMIT_HASH }} \ + -X github.com/xugou/agent/cmd/agent.BuildDate=${{ env.BUILD_DATE }}" \ + -tags netgo,osusergo -o ${{ matrix.artifact_name }} . - name: Build macOS if: matrix.os == 'macos-latest' @@ -64,7 +92,12 @@ jobs: GOARCH: ${{ matrix.arch }} run: | cd agent - go build -v -ldflags="-s -w" -tags netgo,osusergo -o ${{ matrix.artifact_name }} . + go build -v \ + -ldflags="-s -w \ + -X github.com/xugou/agent/cmd/agent.Version=${{ env.VERSION }} \ + -X github.com/xugou/agent/cmd/agent.GitCommit=${{ env.COMMIT_HASH }} \ + -X github.com/xugou/agent/cmd/agent.BuildDate=${{ env.BUILD_DATE }}" \ + -tags netgo,osusergo -o ${{ matrix.artifact_name }} . - name: Build Windows if: matrix.os == 'windows-latest' @@ -73,7 +106,12 @@ jobs: GOARCH: ${{ matrix.arch }} run: | cd agent - go build -v -ldflags="-s -w" -o ${{ matrix.artifact_name }} . + go build -v ` + -ldflags="-s -w ` + -X github.com/xugou/agent/cmd/agent.Version=${{ env.VERSION }} ` + -X github.com/xugou/agent/cmd/agent.GitCommit=${{ env.COMMIT_HASH }} ` + -X github.com/xugou/agent/cmd/agent.BuildDate=${{ env.BUILD_DATE }}" ` + -o ${{ matrix.artifact_name }} . - name: Upload artifact uses: actions/upload-artifact@v4 diff --git a/agent/README.md b/agent/README.md index 39eb85f..8fccfc1 100644 --- a/agent/README.md +++ b/agent/README.md @@ -23,10 +23,6 @@ cd agent go build -o xugou-agent ``` -### 使用预编译二进制文件 - -从 [Releases](https://github.com/xugou/agent/releases) 页面下载适合您系统的预编译二进制文件。 - ## 使用方法 ### 基本命令 diff --git a/agent/cmd/agent/root.go b/agent/cmd/agent/root.go index 6813fd8..8937b5f 100644 --- a/agent/cmd/agent/root.go +++ b/agent/cmd/agent/root.go @@ -36,7 +36,6 @@ func init() { viper.BindPFlag("server", rootCmd.PersistentFlags().Lookup("server")) viper.BindPFlag("token", rootCmd.PersistentFlags().Lookup("token")) viper.BindPFlag("log_level", rootCmd.PersistentFlags().Lookup("log-level")) - viper.BindPFlag("agent_id", rootCmd.PersistentFlags().Lookup("agent-id")) } func initConfig() { diff --git a/agent/cmd/agent/start.go b/agent/cmd/agent/start.go index e900f64..b6dfb60 100644 --- a/agent/cmd/agent/start.go +++ b/agent/cmd/agent/start.go @@ -18,11 +18,11 @@ func init() { startCmd := &cobra.Command{ Use: "start", Short: "启动 Xugou Agent", - Long: `启动 Xugou Agent 开始收集系统信息并上报到服务器`, + Long: `启动 Xugou Agent 开始采集系统信息并上报到服务器`, Run: runStart, } - startCmd.Flags().IntP("interval", "i", 60, "数据收集和上报间隔(秒)") + startCmd.Flags().IntP("interval", "i", 60, "数据采集和上报间隔(秒)") viper.BindPFlag("interval", startCmd.Flags().Lookup("interval")) rootCmd.AddCommand(startCmd) @@ -55,16 +55,8 @@ func runStart(cmd *cobra.Command, args []string) { // 初始化数据收集器和上报器 dataCollector := collector.NewCollector() - var dataReporter reporter.Reporter - - // 根据配置决定使用哪种上报器 - if server == "console" { - dataReporter = reporter.NewConsoleReporter() - fmt.Println("使用控制台上报器") - } else { - dataReporter = reporter.NewHTTPReporter(server, token) - fmt.Println("使用HTTP上报器") - } + dataReporter := reporter.NewHTTPReporter(server, token) + fmt.Println("使用HTTP上报器") // 设置定时器,按指定间隔收集和上报数据 ticker := time.NewTicker(time.Duration(interval) * time.Second) @@ -96,7 +88,7 @@ func collectAndReport(ctx context.Context, c collector.Collector, r reporter.Rep // 收集系统信息 info, err := c.Collect(ctx) if err != nil { - fmt.Printf("收集系统信息失败: %v\n", err) + fmt.Printf("采集系统信息失败: %v\n", err) return } diff --git a/agent/pkg/reporter/reporter.go b/agent/pkg/reporter/reporter.go index 31063e1..cfa64cb 100644 --- a/agent/pkg/reporter/reporter.go +++ b/agent/pkg/reporter/reporter.go @@ -118,7 +118,24 @@ func (r *HTTPReporter) ensureRegistered(ctx context.Context, info *collector.Sys } defer resp.Body.Close() - // 检查响应状态码 + // 检查是否收到"客户端已存在"的响应 + if resp.StatusCode == 400 { + var errorResponse RegisterResponse + decoder := json.NewDecoder(resp.Body) + if err := decoder.Decode(&errorResponse); err != nil { + return fmt.Errorf("解析错误响应失败: %w", err) + } + + if errorResponse.Message == "客户端已存在" { + fmt.Printf("客户端已存在,将直接更新状态,token: %s\n", r.apiToken) + r.registered = true + return nil + } + + return fmt.Errorf("注册失败: %s", errorResponse.Message) + } + + // 检查其他错误状态码 if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("服务器返回错误状态码: %d", resp.StatusCode) } @@ -145,13 +162,8 @@ func (r *HTTPReporter) ensureRegistered(ctx context.Context, info *collector.Sys return nil } -// Report 将系统信息上报到服务器 -func (r *HTTPReporter) Report(ctx context.Context, info *collector.SystemInfo) error { - // 确保客户端已注册 - if err := r.ensureRegistered(ctx, info); err != nil { - return fmt.Errorf("客户端注册或状态更新失败: %w", err) - } - +// reportStatus 将系统信息上报到服务器,不检查注册状态 +func (r *HTTPReporter) reportStatus(ctx context.Context, info *collector.SystemInfo) error { // 提取所有磁盘的总容量和使用量 var diskTotal, diskUsed uint64 for _, disk := range info.DiskInfo { @@ -241,29 +253,17 @@ func (r *HTTPReporter) Report(ctx context.Context, info *collector.SystemInfo) e return nil } -// ConsoleReporter 是将数据输出到控制台的上报器实现 -type ConsoleReporter struct{} - -// NewConsoleReporter 创建一个新的控制台数据上报器 -func NewConsoleReporter() Reporter { - return &ConsoleReporter{} -} - -// Report 将系统信息输出到控制台 -func (r *ConsoleReporter) Report(ctx context.Context, info *collector.SystemInfo) error { - data, err := json.MarshalIndent(info, "", " ") - if err != nil { - return fmt.Errorf("序列化系统信息失败: %w", err) +// Report 将系统信息上报到服务器 +func (r *HTTPReporter) Report(ctx context.Context, info *collector.SystemInfo) error { + // 确保客户端已注册 + if err := r.ensureRegistered(ctx, info); err != nil { + return fmt.Errorf("客户端注册或状态更新失败: %w", err) } - fmt.Println("系统信息收集时间:", info.Timestamp.Format("2006-01-02 15:04:05")) - fmt.Println("主机名:", info.Hostname) - fmt.Println("平台:", info.Platform, info.OS) - fmt.Println("CPU使用率:", info.CPUInfo.Usage, "%") - fmt.Println("内存使用率:", info.MemoryInfo.UsageRate, "%") - fmt.Println("系统负载:", info.LoadInfo.Load1, info.LoadInfo.Load5, info.LoadInfo.Load15) - fmt.Println("详细信息:") - fmt.Println(string(data)) + // 如果注册成功但未直接上报状态(通过客户端已存在的分支),则调用上报 + if r.registered { + return r.reportStatus(ctx, info) + } return nil } diff --git a/backend/src/index.ts b/backend/src/index.ts index 21c7069..acdf219 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -9,36 +9,6 @@ import { ExecutionContext } from 'hono'; // 添加全局变量声明 declare global { var isInitialized: boolean; - namespace NodeJS { - interface ProcessEnv { - NODE_ENV?: string; - PORT?: string; - JWT_SECRET?: string; - } - } -} - -// 定义 D1 数据库类型 -interface D1Database { - prepare(query: string): D1PreparedStatement; - dump(): Promise; - batch(statements: D1PreparedStatement[]): Promise[]>; - exec(query: string): Promise>; -} - -interface D1PreparedStatement { - bind(...values: any[]): D1PreparedStatement; - first(colName?: string): Promise; - run(): Promise>; - all(): Promise>; - raw(): Promise; -} - -interface D1Result { - results?: T[]; - success: boolean; - error?: string; - meta?: object; } // 导入路由 @@ -76,16 +46,6 @@ app.use('*', async (c, next) => { // 公共路由 app.get('/', (c) => c.json({ message: 'XUGOU API 服务正在运行' })); -// 获取 JWT 密钥 -const getJwtSecret = (c: any) => { - // 在 Cloudflare Workers 环境中,使用 env 变量 - if (typeof process === 'undefined') { - return c.env.JWT_SECRET || 'your-secret-key-change-in-production'; - } - // 在 Node.js 环境中,使用 process.env - return process.env.JWT_SECRET || 'your-secret-key-change-in-production'; -}; - // 路由注册 app.route('/api/auth', authRoutes); app.route('/api/monitors', monitorRoutes); diff --git a/backend/src/models/db.ts b/backend/src/models/db.ts index 2620e20..ff5a040 100644 --- a/backend/src/models/db.ts +++ b/backend/src/models/db.ts @@ -24,6 +24,4 @@ export interface D1Result { // 通用绑定类型 export type Bindings = { DB: D1Database; - JWT_SECRET: string; - ENABLE_DB_INIT?: string; }; \ No newline at end of file diff --git a/backend/src/routes/agents.ts b/backend/src/routes/agents.ts index f22baf8..c06ca85 100644 --- a/backend/src/routes/agents.ts +++ b/backend/src/routes/agents.ts @@ -3,7 +3,7 @@ import { jwt } from 'hono/jwt'; import { Context, Next } from 'hono'; import { Bindings } from '../models/db'; import { Agent } from '../models/agent'; -import { getJwtSecret, generateToken } from '../utils/jwt'; +import { getJwtSecret, generateToken, verifyToken } from '../utils/jwt'; import { getAllAgents, getAgentsByUser, @@ -62,7 +62,7 @@ agents.post('/', async (c) => { const { name } = await c.req.json(); const payload = c.get('jwtPayload'); - const token = await generateToken(); + const token = await generateToken(c.env); // 创建新客户端 const newAgent = await createAgent(c.env.DB, name, token, payload.id); @@ -161,49 +161,6 @@ agents.put('/:id', async (c) => { } }); -// 更新客户端状态 -agents.post('/:id/status', async (c) => { - try { - const agentId = Number(c.req.param('id')); - const { - cpu_usage, - memory_total, - memory_used, - disk_total, - disk_used, - network_rx, - network_tx, - hostname, - ip_address, - os, - version - } = await c.req.json(); - - // 更新客户端状态和资源指标 - const result = await updateAgentStatus(c.env.DB, agentId, 'active', { - cpu_usage, - memory_total, - memory_used, - disk_total, - disk_used, - network_rx, - network_tx, - hostname, - ip_address, - os, - version - }); - - return c.json({ - success: true, - message: '客户端状态已更新' - }); - } catch (error) { - console.error('更新客户端状态错误:', error); - return c.json({ success: false, message: '更新客户端状态失败' }, 500); - } -}); - // 删除客户端 agents.delete('/:id', async (c) => { try { @@ -235,48 +192,11 @@ agents.delete('/:id', async (c) => { } }); -// 重新生成客户端令牌 -agents.post('/:id/token', async (c) => { - try { - const agentId = Number(c.req.param('id')); - const payload = c.get('jwtPayload'); - - // 获取客户端信息 - const agent = await getAgentById(c.env.DB, agentId); - - if (!agent) { - return c.json({ success: false, message: '客户端不存在' }, 404); - } - - // 检查权限 - if (payload.role !== 'admin' && agent.created_by !== payload.id) { - return c.json({ success: false, message: '无权为此客户端重新生成令牌' }, 403); - } - - // 生成新令牌 - const newToken = await generateToken(); - - // 更新客户端令牌 - const result = await updateAgentToken(c.env.DB, agent.id, newToken); - - return c.json({ - success: true, - message: '客户端令牌已重新生成', - token: newToken - }); - } catch (error) { - console.error('重新生成客户端令牌错误:', error); - return c.json({ success: false, message: '重新生成客户端令牌失败' }, 500); - } -}); - -// 生成临时注册Token +// 生成客户端Token agents.post('/token/generate', async (c) => { try { - const payload = c.get('jwtPayload'); - // 生成新令牌 - const newToken = await generateToken(); + const newToken = await generateToken(c.env); // 可以选择将此token存储在临时表中,或者使用其他方式验证(例如,设置过期时间) // 这里为简化操作,只返回令牌 @@ -301,29 +221,29 @@ agents.post('/register', async (c) => { return c.json({ success: false, message: '缺少注册令牌' }, 400); } + + // 通过token查找客户端 + const existingAgent = await getAgentByToken(c.env.DB, token); + + if (existingAgent) { + return c.json({ success: false, message: '客户端已存在' }, 400); + } + + + // 验证令牌 + const tokenVerification = await verifyToken(token, c.env); + + // 如果令牌无效,返回错误 + if (!tokenVerification.valid) { + return c.json({ + success: false, + message: `注册令牌无效: ${tokenVerification.message}` + }, 401); + } + // 查找管理员用户作为客户端创建者 const adminId = await getAdminUserId(c.env.DB); - // 查找是否已存在使用相同token的客户端 - const existingAgent = await getAgentByToken(c.env.DB, token); - - if (existingAgent) { - // 如果客户端已存在,更新其状态信息 - await updateAgent(c.env.DB, existingAgent.id, { - status: 'active', - hostname, - ip_address, - os, - version - }); - - return c.json({ - success: true, - message: '客户端状态更新成功', - agent: { id: existingAgent.id } - }); - } - // 如果客户端不存在,则创建新客户端 const newAgent = await createAgent( c.env.DB, diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts index 8ef5693..66f6633 100644 --- a/backend/src/routes/auth.ts +++ b/backend/src/routes/auth.ts @@ -8,7 +8,6 @@ import { getUserByUsername, createUser, getUserById } from '../db/auth'; // 导入 D1 数据库类型 type Bindings = { DB: D1Database; - JWT_SECRET: string; }; interface D1Database { diff --git a/backend/src/routes/status.ts b/backend/src/routes/status.ts index 2b6e334..cb6d506 100644 --- a/backend/src/routes/status.ts +++ b/backend/src/routes/status.ts @@ -4,10 +4,8 @@ import { cors } from 'hono/cors'; import { Bindings } from '../models/db'; import { - StatusPageConfig as DbStatusPageConfig, Monitor, Agent, - getUserStatusPageConfig, getConfigMonitors, getConfigAgents, getStatusPageConfigById, @@ -26,7 +24,7 @@ import { getAdminUserId, createDefaultConfig } from '../db/status'; - +import { getJwtSecret } from '../utils/jwt'; // 状态页配置接口 interface StatusPageConfig { title: string; @@ -48,18 +46,10 @@ const adminRoutes = new Hono<{ Bindings: Bindings }>(); // 使用中间件验证JWT(仅对管理员路由) adminRoutes.use('/*', async (c, next) => { - try { - // 使用环境变量中的JWT密钥进行验证 - const jwtMiddleware = jwt({ - secret: c.env.JWT_SECRET, - }); - - // 执行JWT认证 - return await jwtMiddleware(c, next); - } catch (error) { - console.error('JWT验证失败:', error); - return c.json({ error: '未授权' }, 401); - } + const jwtMiddleware = jwt({ + secret: getJwtSecret(c) + }); + return jwtMiddleware(c, next); }); // 获取状态页配置(管理员) diff --git a/backend/src/utils/jwt.ts b/backend/src/utils/jwt.ts index ce3a57e..4a3e45a 100644 --- a/backend/src/utils/jwt.ts +++ b/backend/src/utils/jwt.ts @@ -10,22 +10,174 @@ * @returns JWT密钥 */ export const getJwtSecret = (c: any): string => { - // 在Cloudflare Workers环境中,使用env变量 - if (typeof process === 'undefined') { - return c.env.JWT_SECRET || 'your-secret-key-change-in-production'; - } - // 在Node.js环境中,使用process.env - return process.env.JWT_SECRET || 'your-secret-key-change-in-production'; + const { id: versionId, tag: versionTag, timestamp: versionTimestamp } = c.env.CF_VERSION_METADATA; + return versionId || 'your-secret-key-change-in-production'; }; /** * 生成随机令牌 - * 生成用于API密钥或认证令牌的随机字符串 + * 生成用于API密钥或认证令牌的随机字符串,包含时间戳、前缀和签名 * + * @param env 环境变量或上下文对象,用于获取密钥 * @returns 生成的随机令牌 */ -export async function generateToken(): Promise { - const array = new Uint8Array(32); +export async function generateToken(env?: any): Promise { + console.log('=== 开始生成令牌 ==='); + console.log('env 参数:', env ? '已提供' : '未提供'); + + // 生成随机部分 + const array = new Uint8Array(16); crypto.getRandomValues(array); - return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join(''); + const randomPart = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join(''); + console.log('生成的随机部分:', randomPart); + + // 添加时间戳 + const timestamp = Date.now().toString(36); + console.log('时间戳 (Base36):', timestamp, '原始时间戳:', Date.now()); + + // 添加特定前缀,用于标识这是一个XUGOU系统的令牌 + const prefix = 'xugou'; + console.log('使用前缀:', prefix); + + // 创建基本令牌 + const baseToken = `${prefix}_${timestamp}_${randomPart}`; + console.log('基本令牌 (无签名):', baseToken); + + + // 获取密钥 + if (!env || !env.CF_VERSION_METADATA) { + console.error('错误: env.CF_VERSION_METADATA 不存在,无法生成签名'); + return baseToken; + } + + const { id: versionId, tag: versionTag, timestamp: versionTimestamp } = env.CF_VERSION_METADATA; + console.log("generateToken 使用的 versionId:", versionId); + console.log("generateToken 版本信息:", { tag: versionTag, timestamp: versionTimestamp }); + + try { + // 创建签名 + const msgUint8 = new TextEncoder().encode(baseToken + versionId); + console.log('签名数据长度:', msgUint8.length, '字节'); + + const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const signature = hashArray.map(b => b.toString(16).padStart(2, '0')).join('').substring(0, 16); + console.log('生成的签名:', signature); + + // 返回带签名的令牌 + const fullToken = `${baseToken}.${signature}`; + console.log('完整令牌:', fullToken); + console.log('=== 令牌生成完成 ==='); + return fullToken; + } catch (error) { + console.error('生成令牌签名时出错:', error); + return baseToken; + } +} + +/** + * 验证令牌是否有效 + * 验证令牌的格式和签名是否正确 + * + * @param token 需要验证的令牌 + * @param env 环境变量或上下文对象,用于获取密钥 + * @returns 验证结果对象,包含是否有效和相关信息 + */ +export async function verifyToken( + token: string, + env: any, +): Promise<{ valid: boolean; message?: string; timestamp?: number; payload?: any }> { + console.log('=== 开始验证令牌 ==='); + console.log('待验证令牌:', token); + + try { + // 首先验证令牌格式 + const parts = token.split('.'); + console.log('令牌部分数量:', parts.length); + + // 如果令牌没有签名部分(即不包含.),或者格式不正确,则无效 + if (parts.length !== 2) { + console.log('验证失败: 令牌格式无效 (没有点号分隔符或分隔符数量不正确)'); + return { valid: false, message: '令牌格式无效' }; + } + + // 解析令牌各部分 + const [baseToken, signature] = parts; + console.log('基本令牌部分:', baseToken); + console.log('签名部分:', signature); + + const baseTokenParts = baseToken.split('_'); + console.log('基本令牌组件数量:', baseTokenParts.length); + + // 验证基本令牌格式 + if (baseTokenParts.length !== 3) { + console.log('验证失败: 基本令牌格式无效 (不包含3个由下划线分隔的部分)'); + return { valid: false, message: '令牌格式无效' }; + } + + const [prefix, timestampStr, randomPart] = baseTokenParts; + console.log('解析出的令牌组件:', { prefix, timestampStr, randomPart }); + + // 验证前缀 + if (prefix !== 'xugou') { + console.log(`验证失败: 令牌前缀无效 (期望 'xugou', 实际为 '${prefix}')`); + return { valid: false, message: '令牌前缀无效' }; + } + + // 解析时间戳 + const timestamp = parseInt(timestampStr, 36); + console.log('解析的时间戳 (Base36 -> 数字):', timestampStr, '->', timestamp); + console.log('时间戳对应的日期:', new Date(timestamp).toISOString()); + + // 验证时间戳是否为有效数字 + if (isNaN(timestamp)) { + console.log('验证失败: 时间戳无效 (不是有效的Base36数字)'); + return { valid: false, message: '令牌时间戳无效' }; + } + + // 获取密钥 + if (!env || !env.CF_VERSION_METADATA) { + console.error('错误: env.CF_VERSION_METADATA 不存在,无法验证签名'); + return { valid: false, message: '环境变量不完整,无法验证签名' }; + } + + const { id: versionId, tag: versionTag, timestamp: versionTimestamp } = env.CF_VERSION_METADATA; + console.log("verifyToken 使用的 versionId:", versionId); + console.log("verifyToken 版本信息:", { tag: versionTag, timestamp: versionTimestamp }); + + // 重新计算签名并验证 + const msgUint8 = new TextEncoder().encode(baseToken + versionId); + console.log('验证签名数据长度:', msgUint8.length, '字节'); + + const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const calculatedSignature = hashArray.map(b => b.toString(16).padStart(2, '0')).join('').substring(0, 16); + console.log('计算出的签名:', calculatedSignature); + console.log('令牌中的签名:', signature); + console.log('签名是否匹配:', calculatedSignature === signature); + + // 比较计算出的签名和令牌中的签名 + if (calculatedSignature !== signature) { + console.log('验证失败: 签名不匹配'); + return { valid: false, message: '令牌签名无效' }; + } + + // 如果所有验证都通过,令牌有效 + console.log('验证成功: 令牌有效'); + const result = { + valid: true, + timestamp, + payload: { + prefix, + timestamp, + randomPart + } + }; + console.log('验证结果:', result); + console.log('=== 令牌验证完成 ==='); + return result; + } catch (error) { + console.error('令牌验证过程出错:', error); + return { valid: false, message: '令牌验证过程出错' }; + } } \ No newline at end of file diff --git a/backend/wrangler.toml b/backend/wrangler.toml index 1f664d4..1432508 100644 --- a/backend/wrangler.toml +++ b/backend/wrangler.toml @@ -11,3 +11,6 @@ crons = ["* * * * *"] binding = "DB" database_name = "xugou_db" database_id = "32a2ca5f-3a77-4b08-965e-804612557880" + +[version_metadata] +binding = "CF_VERSION_METADATA" \ No newline at end of file