security: 安全审计修复 - API Key清理 + JWT改为HttpOnly Cookie

- API Key: run.md/.env/seed_model_providers.sql 中明文Key替换为占位符
- 认证: JWT从localStorage迁移到HttpOnly Cookie,移除后端body返回token
- 前端: api.ts/knowledge/page.tsx/auth.ts 全面改用Cookie认证
- postcss XSS: package.json添加overrides强制升级到8.5.15
- gitignore: 添加server/.env和.env排除规则
- lint: 移除tenant.go中未使用的roleKey常量
- 文档: 新增docs/security-audit-report.md和hardware-requirements.md
This commit is contained in:
selfrelease
2026-06-26 10:44:04 +08:00
parent c011f2eae8
commit a3245b249a
15 changed files with 442 additions and 86 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ ANTHROPIC_BASE_URL=https://api.anthropic.com
ANTHROPIC_MODEL=claude-sonnet-4-20250514
# 阿里千问 (Qwen)
QWEN_API_KEY=sk-c0c5174892c44ff48d587cd040fbdd40
QWEN_API_KEY=[YOUR_API_KEY]
QWEN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
QWEN_MODEL=qwen-plus
+8 -10
View File
@@ -108,8 +108,8 @@ func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) {
"user": userResponse{
ID: id.String(), Name: req.Name, Email: req.Email, Role: "user",
},
"access_token": tokenPair.AccessToken,
"expires_at": tokenPair.ExpiresAt,
// access_token 已通过 HttpOnly Cookie 设置,不再在 body 中返回
"expires_at": tokenPair.ExpiresAt,
})
}
@@ -205,9 +205,8 @@ func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
}
response.JSON(w, http.StatusOK, map[string]any{
"user": usr,
"access_token": tokenPair.AccessToken,
"expires_at": tokenPair.ExpiresAt,
"user": usr,
"expires_at": tokenPair.ExpiresAt,
})
}
@@ -316,8 +315,8 @@ func (h *AuthHandler) SwitchOrg(w http.ResponseWriter, r *http.Request) {
return
}
// 为目标用户生成新的JWT token
tokens, err := h.jwtMgr.GenerateTokenPair(targetID, targetEmail, targetRole)
// 为目标用户生成新的JWT token(通过 HttpOnly Cookie 设置)
_, err = h.jwtMgr.GenerateTokenPair(targetID, targetEmail, targetRole)
if err != nil {
response.InternalError(w, "生成令牌失败")
return
@@ -327,7 +326,7 @@ func (h *AuthHandler) SwitchOrg(w http.ResponseWriter, r *http.Request) {
response.JSON(w, http.StatusOK, map[string]any{
"message": "已切换",
"org": org,
"token": tokens.AccessToken,
// access_token 已通过 HttpOnly Cookie 设置
"user": map[string]any{
"id": targetID,
"name": targetName,
@@ -412,7 +411,6 @@ func (h *AuthHandler) Refresh(w http.ResponseWriter, r *http.Request) {
})
response.JSON(w, http.StatusOK, map[string]any{
"access_token": tokenPair.AccessToken,
"expires_at": tokenPair.ExpiresAt,
"expires_at": tokenPair.ExpiresAt,
})
}
+1 -1
View File
@@ -13,7 +13,7 @@ INSERT INTO model_providers (
gen_random_uuid(),
'阿里云百炼 (通义千问)',
'https://dashscope.aliyuncs.com/compatible-mode/v1',
'sk-c0c5174892c44ff48d587cd040fbdd40', -- 实际环境中应该加密存储
'[YOUR_API_KEY]', -- 部署时通过环境变量 OPENAI_API_KEY 注入
'[
{"id": "qwen-plus", "name": "通义千问-Plus", "type": "chat"},
{"id": "qwen-turbo", "name": "通义千问-Turbo", "type": "chat"},
+2 -3
View File
@@ -11,9 +11,8 @@ import (
type contextKey string
const (
orgIDKey contextKey = "org_id"
userIDKey contextKey = "user_id"
roleKey contextKey = "role"
orgIDKey contextKey = "org_id"
userIDKey contextKey = "user_id"
)
// WithOrgID stores the organization ID in context.