diff --git a/controllers/admin/auth.go b/controllers/admin/auth.go index bd44df7..088e1b2 100644 --- a/controllers/admin/auth.go +++ b/controllers/admin/auth.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "net/http" - "strconv" "strings" "time" @@ -19,9 +18,10 @@ import ( // LoginPageHandler 管理员登录页渲染处理器 // - 如果已登录则重定向到 /admin // - 否则渲染 web/template/admin/login.html 模板 +// - 自动清理失效的JWT Cookie,避免刷新时的问题 func LoginPageHandler(w http.ResponseWriter, r *http.Request) { - // 已登录直接跳转到后台布局 - if IsAdminAuthenticated(r) { + // 使用带清理功能的JWT校验,避免失效Cookie在登录页面造成问题 + if IsAdminAuthenticatedWithCleanup(w, r) { http.Redirect(w, r, "/admin", http.StatusFound) return } @@ -118,6 +118,20 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) { // - 确保令牌完全失效 func LogoutHandler(w http.ResponseWriter, r *http.Request) { // 清理JWT Cookie + clearInvalidJWTCookie(w) + + // 可选:将JWT令牌加入黑名单(需要Redis或数据库支持) + // 这里可以实现JWT黑名单机制 + + utils.JsonResponse(w, http.StatusOK, true, "已退出登录", map[string]interface{}{ + "redirect": "/admin/login", + }) +} + +// clearInvalidJWTCookie 清理失效的JWT Cookie +// - 统一的Cookie清理函数,确保一致性 +// - 在JWT校验失败时自动调用,提升安全性和用户体验 +func clearInvalidJWTCookie(w http.ResponseWriter) { cookie := &http.Cookie{ Name: "admin_session", Value: "", @@ -128,13 +142,6 @@ func LogoutHandler(w http.ResponseWriter, r *http.Request) { Expires: time.Unix(0, 0), // 确保过期 } http.SetCookie(w, cookie) - - // 可选:将JWT令牌加入黑名单(需要Redis或数据库支持) - // 这里可以实现JWT黑名单机制 - - utils.JsonResponse(w, http.StatusOK, true, "已退出登录", map[string]interface{}{ - "redirect": "/admin/login", - }) } // JWT密钥(生产环境应从配置文件或环境变量读取) @@ -142,9 +149,10 @@ var jwtSecret = []byte(viper.GetString("security.jwt_secret")) // JWTClaims JWT载荷结构 type JWTClaims struct { - UserID uint `json:"user_id"` - Username string `json:"username"` - Role int `json:"role"` + UserUUID string `json:"user_uuid"` + Username string `json:"username"` + Role int `json:"role"` + PasswordHash string `json:"password_hash"` // 密码哈希摘要,用于验证密码是否被修改 jwt.RegisteredClaims } @@ -153,16 +161,20 @@ type JWTClaims struct { // - 设置24小时过期时间 // - 使用HMAC-SHA256签名 func generateJWTToken(user models.User) (string, error) { + // 生成密码哈希摘要(使用SHA256) + passwordHashDigest := utils.GenerateSHA256Hash(user.Password) + claims := JWTClaims{ - UserID: user.ID, - Username: user.Username, - Role: user.Role, + UserUUID: user.UUID, + Username: user.Username, + Role: user.Role, + PasswordHash: passwordHashDigest, // 包含密码哈希摘要 RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), IssuedAt: jwt.NewNumericDate(time.Now()), NotBefore: jwt.NewNumericDate(time.Now()), Issuer: "凌动技术", - Subject: strconv.Itoa(int(user.ID)), + Subject: user.UUID, }, } @@ -213,8 +225,96 @@ func IsAdminAuthenticated(r *http.Request) bool { return false } - // 可选:进一步验证用户是否仍然存在且有效 - // 这里可以添加数据库查询来验证用户状态 + // 验证用户是否仍然存在于数据库中 + db, err := database.GetDB() + if err != nil { + return false + } + + var user models.User + if dbErr := db.Where("uuid = ? AND role = 0", claims.UserUUID).First(&user).Error; dbErr != nil { + // 记录安全事件:用户不存在但持有有效JWT令牌 + fmt.Printf("[SECURITY WARNING] Invalid JWT token detected - User not found: UUID=%s, Username=%s, IP=%s\n", + claims.UserUUID, claims.Username, r.RemoteAddr) + return false + } + + // 验证用户名是否匹配(防止用户名被修改后仍使用旧令牌) + if user.Username != claims.Username { + // 记录安全事件:用户名不匹配 + fmt.Printf("[SECURITY WARNING] Username mismatch detected - Token username=%s, DB username=%s, UUID=%s, IP=%s\n", + claims.Username, user.Username, claims.UserUUID, r.RemoteAddr) + return false + } + + // 验证密码哈希是否匹配(防止密码被修改后仍使用旧令牌) + currentPasswordHash := utils.GenerateSHA256Hash(user.Password) + if claims.PasswordHash != currentPasswordHash { + // 记录安全事件:密码哈希不匹配,可能密码已被修改 + fmt.Printf("[SECURITY WARNING] Password hash mismatch detected - Token may be invalid due to password change: UUID=%s, Username=%s, IP=%s\n", + claims.UserUUID, claims.Username, r.RemoteAddr) + return false + } + + return true +} + +// IsAdminAuthenticatedWithCleanup 带自动清理功能的JWT校验函数 +// - 当JWT校验失败时,自动清理失效的Cookie +// - 适用于API接口等需要清理失效令牌的场景 +func IsAdminAuthenticatedWithCleanup(w http.ResponseWriter, r *http.Request) bool { + cookie, err := r.Cookie("admin_session") + if err != nil || cookie.Value == "" { + return false + } + + // 解析并验证JWT令牌 + claims, err := parseJWTToken(cookie.Value) + if err != nil { + // JWT解析失败,清理失效Cookie + clearInvalidJWTCookie(w) + return false + } + + // 验证用户角色(只允许管理员角色=0) + if claims.Role != 0 { + clearInvalidJWTCookie(w) + return false + } + + // 验证用户是否仍然存在于数据库中 + db, err := database.GetDB() + if err != nil { + return false + } + + var user models.User + if dbErr := db.Where("uuid = ? AND role = 0", claims.UserUUID).First(&user).Error; dbErr != nil { + // 记录安全事件并清理失效Cookie + fmt.Printf("[SECURITY WARNING] Invalid JWT token detected - User not found: UUID=%s, Username=%s, IP=%s\n", + claims.UserUUID, claims.Username, r.RemoteAddr) + clearInvalidJWTCookie(w) + return false + } + + // 验证用户名是否匹配(防止用户名被修改后仍使用旧令牌) + if user.Username != claims.Username { + // 记录安全事件并清理失效Cookie + fmt.Printf("[SECURITY WARNING] Username mismatch detected - Token username=%s, DB username=%s, UUID=%s, IP=%s\n", + claims.Username, user.Username, claims.UserUUID, r.RemoteAddr) + clearInvalidJWTCookie(w) + return false + } + + // 验证密码哈希是否匹配(防止密码被修改后仍使用旧令牌) + currentPasswordHash := utils.GenerateSHA256Hash(user.Password) + if claims.PasswordHash != currentPasswordHash { + // 记录安全事件并清理失效Cookie + fmt.Printf("[SECURITY WARNING] Password hash mismatch detected - Token may be invalid due to password change: UUID=%s, Username=%s, IP=%s\n", + claims.UserUUID, claims.Username, r.RemoteAddr) + clearInvalidJWTCookie(w) + return false + } return true } @@ -238,6 +338,37 @@ func GetCurrentAdminUser(r *http.Request) (*JWTClaims, error) { return nil, fmt.Errorf("权限不足") } + // 验证用户是否仍然存在于数据库中 + db, err := database.GetDB() + if err != nil { + return nil, fmt.Errorf("数据库连接失败") + } + + var user models.User + if dbErr := db.Where("uuid = ? AND role = 0", claims.UserUUID).First(&user).Error; dbErr != nil { + // 记录安全事件:用户不存在但持有有效JWT令牌 + fmt.Printf("[SECURITY WARNING] Invalid JWT token detected in GetCurrentAdminUser - User not found: UUID=%s, Username=%s, IP=%s\n", + claims.UserUUID, claims.Username, r.RemoteAddr) + return nil, fmt.Errorf("用户不存在或权限已变更") + } + + // 验证用户名是否匹配(防止用户名被修改后仍使用旧令牌) + if user.Username != claims.Username { + // 记录安全事件:用户名不匹配 + fmt.Printf("[SECURITY WARNING] Username mismatch detected in GetCurrentAdminUser - Token username=%s, DB username=%s, UUID=%s, IP=%s\n", + claims.Username, user.Username, claims.UserUUID, r.RemoteAddr) + return nil, fmt.Errorf("用户信息已变更,请重新登录") + } + + // 验证密码哈希是否匹配(防止密码被修改后仍使用旧令牌) + currentPasswordHash := utils.GenerateSHA256Hash(user.Password) + if claims.PasswordHash != currentPasswordHash { + // 记录安全事件:密码哈希不匹配,可能密码已被修改 + fmt.Printf("[SECURITY WARNING] Password hash mismatch detected in GetCurrentAdminUser - Token may be invalid due to password change: UUID=%s, Username=%s, IP=%s\n", + claims.UserUUID, claims.Username, r.RemoteAddr) + return nil, fmt.Errorf("密码已变更,请重新登录") + } + return claims, nil } @@ -260,13 +391,44 @@ func GetCurrentAdminUserWithRefresh(w http.ResponseWriter, r *http.Request) (*JW return nil, false, fmt.Errorf("权限不足") } + // 验证用户是否仍然存在于数据库中 + db, err := database.GetDB() + if err != nil { + return nil, false, fmt.Errorf("数据库连接失败") + } + + var user models.User + if dbErr := db.Where("uuid = ? AND role = 0", claims.UserUUID).First(&user).Error; dbErr != nil { + // 记录安全事件:用户不存在但持有有效JWT令牌 + fmt.Printf("[SECURITY WARNING] Invalid JWT token detected in GetCurrentAdminUserWithRefresh - User not found: UUID=%s, Username=%s, IP=%s\n", + claims.UserUUID, claims.Username, r.RemoteAddr) + return nil, false, fmt.Errorf("用户不存在或权限已变更") + } + + // 验证用户名是否匹配(防止用户名被修改后仍使用旧令牌) + if user.Username != claims.Username { + // 记录安全事件:用户名不匹配 + fmt.Printf("[SECURITY WARNING] Username mismatch detected in GetCurrentAdminUserWithRefresh - Token username=%s, DB username=%s, UUID=%s, IP=%s\n", + claims.Username, user.Username, claims.UserUUID, r.RemoteAddr) + return nil, false, fmt.Errorf("用户信息已变更,请重新登录") + } + + // 验证密码哈希是否匹配(防止密码被修改后仍使用旧令牌) + currentPasswordHash := utils.GenerateSHA256Hash(user.Password) + if claims.PasswordHash != currentPasswordHash { + // 记录安全事件:密码哈希不匹配,可能密码已被修改 + fmt.Printf("[SECURITY WARNING] Password hash mismatch detected in GetCurrentAdminUserWithRefresh - Token may be invalid due to password change: UUID=%s, Username=%s, IP=%s\n", + claims.UserUUID, claims.Username, r.RemoteAddr) + return nil, false, fmt.Errorf("密码已变更,请重新登录") + } + // 检查是否需要刷新令牌(根据配置的阈值) refreshed := false refreshThreshold := time.Duration(viper.GetInt("security.jwt_refresh_threshold_hours")) * time.Hour if time.Until(claims.ExpiresAt.Time) < refreshThreshold { // 生成新的JWT令牌 user := models.User{ - ID: claims.UserID, + UUID: claims.UserUUID, Username: claims.Username, Role: claims.Role, } @@ -301,6 +463,9 @@ func AdminAuthRequired(next http.HandlerFunc) http.HandlerFunc { // 尝试获取用户信息并自动刷新令牌 claims, refreshed, err := GetCurrentAdminUserWithRefresh(w, r) if err != nil { + // 自动清理失效的JWT Cookie,提升安全性和用户体验 + clearInvalidJWTCookie(w) + // 中文注释:区分普通页面请求与AJAX/JSON请求 // - 对 AJAX/JSON:直接返回 401 JSON,便于前端处理(如提示重新登录) // - 对普通页面:保持原有重定向到登录页 diff --git a/controllers/admin/handlers.go b/controllers/admin/handlers.go index 79b4aa6..314b270 100644 --- a/controllers/admin/handlers.go +++ b/controllers/admin/handlers.go @@ -13,8 +13,9 @@ import ( // AdminIndexHandler /admin 与 /admin/ 根路径入口 // - 未登录:重定向到 /admin/login // - 已登录:渲染后台布局页(或重定向到 /admin/layout) +// - 自动清理失效的JWT Cookie func AdminIndexHandler(w http.ResponseWriter, r *http.Request) { - if IsAdminAuthenticated(r) { + if IsAdminAuthenticatedWithCleanup(w, r) { // 直接渲染布局页,保持URL为 /admin AdminLayoutHandler(w, r) return diff --git a/controllers/admin/user.go b/controllers/admin/user.go index 0b025b2..57f30f7 100644 --- a/controllers/admin/user.go +++ b/controllers/admin/user.go @@ -2,6 +2,7 @@ package admin import ( "encoding/json" + "fmt" "net/http" "networkDev/database" "networkDev/models" @@ -16,7 +17,7 @@ func UserFragmentHandler(w http.ResponseWriter, r *http.Request) { } // UserProfileQueryHandler 查询当前登录管理员的基本信息 -// - 返回 id/username/role 三个字段 +// - 返回 uuid/username/role/created_at 四个字段 // - 自动刷新接近过期的JWT令牌 func UserProfileQueryHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { @@ -30,10 +31,24 @@ func UserProfileQueryHandler(w http.ResponseWriter, r *http.Request) { return } + // 查询用户完整信息以获取创建时间 + db, err := database.GetDB() + if err != nil { + utils.JsonResponse(w, http.StatusInternalServerError, false, "数据库连接失败", nil) + return + } + + var user models.User + if dbErr := db.Where("uuid = ?", claims.UserUUID).First(&user).Error; dbErr != nil { + utils.JsonResponse(w, http.StatusNotFound, false, "用户不存在", nil) + return + } + utils.JsonResponse(w, http.StatusOK, true, "ok", map[string]interface{}{ - "id": claims.UserID, - "username": claims.Username, - "role": claims.Role, + "uuid": user.UUID, + "username": user.Username, + "role": user.Role, + "created_at": user.CreatedAt, }) } @@ -91,7 +106,7 @@ func UserPasswordUpdateHandler(w http.ResponseWriter, r *http.Request) { // 查询当前用户 var user models.User - if dbErr := db.First(&user, claims.UserID).Error; dbErr != nil { + if dbErr := db.Where("uuid = ?", claims.UserUUID).First(&user).Error; dbErr != nil { utils.JsonResponse(w, http.StatusNotFound, false, "用户不存在", nil) return } @@ -105,30 +120,59 @@ func UserPasswordUpdateHandler(w http.ResponseWriter, r *http.Request) { // 生成新的密码盐值 newSalt, err := utils.GenerateRandomSalt() if err != nil { + // 添加详细错误日志 + fmt.Printf("生成密码盐失败: %v\n", err) utils.JsonResponse(w, http.StatusInternalServerError, false, "生成密码盐失败", nil) return } + fmt.Printf("成功生成新盐值,长度: %d\n", len(newSalt)) // 使用新盐值生成密码哈希 hash, err := utils.HashPasswordWithSalt(body.NewPassword, newSalt) if err != nil { + // 添加详细错误日志 + fmt.Printf("生成密码哈希失败: %v, 密码长度: %d, 盐值长度: %d\n", err, len(body.NewPassword), len(newSalt)) utils.JsonResponse(w, http.StatusInternalServerError, false, "生成密码哈希失败", nil) return } + fmt.Printf("成功生成密码哈希,长度: %d\n", len(hash)) // 更新密码和盐值 - if err := db.Model(&models.User{}).Where("id = ?", claims.UserID).Updates(map[string]interface{}{ + if dbErr := db.Model(&models.User{}).Where("uuid = ?", claims.UserUUID).Updates(map[string]interface{}{ "password": hash, "password_salt": newSalt, - }).Error; err != nil { + }).Error; dbErr != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "更新密码失败", nil) return } - // 可选:安全起见,通知前端跳转到登录页 - utils.JsonResponse(w, http.StatusOK, true, "密码修改成功,请重新登录", map[string]interface{}{ - "redirect": "/admin/login", - }) + // 重新查询用户信息(包含新密码) + var updatedUser models.User + if dbErr := db.Where("uuid = ?", claims.UserUUID).First(&updatedUser).Error; dbErr != nil { + utils.JsonResponse(w, http.StatusInternalServerError, false, "查询用户信息失败", nil) + return + } + + // 重新生成JWT令牌(包含新的密码哈希摘要) + newToken, err := generateJWTToken(updatedUser) + if err != nil { + utils.JsonResponse(w, http.StatusInternalServerError, false, "生成新令牌失败", nil) + return + } + + // 更新Cookie + cookie := &http.Cookie{ + Name: "admin_session", + Value: newToken, + Path: "/", + HttpOnly: true, + Secure: false, // 生产环境应设置为true(HTTPS) + MaxAge: 24 * 60 * 60, // 24小时 + } + http.SetCookie(w, cookie) + + // 密码修改成功,已重新生成JWT令牌 + utils.JsonResponse(w, http.StatusOK, true, "密码修改成功", nil) } // UserProfileUpdateHandler 修改当前登录管理员的用户名 @@ -173,9 +217,9 @@ func UserProfileUpdateHandler(w http.ResponseWriter, r *http.Request) { return } - // 检查唯一性:排除当前用户ID + // 检查唯一性:排除当前用户UUID var cnt int64 - if dbErr := db.Model(&models.User{}).Where("username = ? AND id <> ?", username, claims.UserID).Count(&cnt).Error; dbErr != nil { + if dbErr := db.Model(&models.User{}).Where("username = ? AND uuid <> ?", username, claims.UserUUID).Count(&cnt).Error; dbErr != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "检查用户名唯一性失败", nil) return } @@ -199,7 +243,7 @@ func UserProfileUpdateHandler(w http.ResponseWriter, r *http.Request) { } // 查询当前用户并校验旧密码 var user models.User - if dbErr := db.First(&user, claims.UserID).Error; dbErr != nil { + if dbErr := db.Where("uuid = ?", claims.UserUUID).First(&user).Error; dbErr != nil { utils.JsonResponse(w, http.StatusNotFound, false, "用户不存在", nil) return } @@ -210,13 +254,13 @@ func UserProfileUpdateHandler(w http.ResponseWriter, r *http.Request) { } // 执行更新 - if dbErr := db.Model(&models.User{}).Where("id = ?", claims.UserID).Update("username", username).Error; dbErr != nil { + if dbErr := db.Model(&models.User{}).Where("uuid = ?", claims.UserUUID).Update("username", username).Error; dbErr != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "更新用户名失败", nil) return } // 重新签发JWT并写入Cookie - newUser := models.User{ID: claims.UserID, Username: username, Role: claims.Role} + newUser := models.User{UUID: claims.UserUUID, Username: username, Role: claims.Role} token, err := generateJWTToken(newUser) if err != nil { utils.JsonResponse(w, http.StatusInternalServerError, false, "生成新令牌失败", nil) diff --git a/controllers/admin/variable.go b/controllers/admin/variable.go index e0db73e..77c5dc1 100644 --- a/controllers/admin/variable.go +++ b/controllers/admin/variable.go @@ -40,8 +40,13 @@ func VariableListHandler(w http.ResponseWriter, r *http.Request) { // 获取应用UUID参数(用于按应用筛选变量) appUUID := strings.TrimSpace(r.URL.Query().Get("app_uuid")) - // 获取别名搜索参数 - alias := strings.TrimSpace(r.URL.Query().Get("alias")) + // 获取搜索关键词参数(支持编号、别名、数据的综合搜索) + search := strings.TrimSpace(r.URL.Query().Get("search")) + + // 兼容旧的别名搜索参数 + if search == "" { + search = strings.TrimSpace(r.URL.Query().Get("alias")) + } // 构建查询 db, err := database.GetDB() @@ -59,9 +64,10 @@ func VariableListHandler(w http.ResponseWriter, r *http.Request) { query = query.Where("app_uuid = ?", appUUID) } - // 如果指定了别名搜索,则按别名模糊搜索 - if alias != "" { - query = query.Where("alias LIKE ?", "%"+alias+"%") + // 如果指定了搜索关键词,则在编号、别名、数据、备注中进行模糊搜索 + if search != "" { + query = query.Where("number LIKE ? OR alias LIKE ? OR data LIKE ? OR remark LIKE ?", + "%"+search+"%", "%"+search+"%", "%"+search+"%", "%"+search+"%") } // 获取总数 diff --git a/database/seed_user.go b/database/seed_user.go index 668ca63..d387e74 100644 --- a/database/seed_user.go +++ b/database/seed_user.go @@ -10,7 +10,7 @@ import ( // SeedDefaultAdmin 初始化默认管理员账号 // - 如果用户名为 admin 的用户已存在,则跳过 // - 如不存在,则创建用户名为 admin、密码为 admin123(以 bcrypt 哈希存储)、角色 Role=0 的管理员 -// - 根据需求:默认 admin 用户的 ID 固定为 10000 +// - ID和UUID将自动生成 func SeedDefaultAdmin() error { db, err := GetDB() if err != nil { @@ -38,9 +38,8 @@ func SeedDefaultAdmin() error { return err } - // 创建默认管理员(指定固定 ID=10000) + // 创建默认管理员(ID和UUID将自动生成) admin := models.User{ - ID: 10000, Username: "admin", Password: hash, PasswordSalt: salt, @@ -49,6 +48,6 @@ func SeedDefaultAdmin() error { if err := db.Create(&admin).Error; err != nil { return err } - logrus.WithField("username", "admin").WithField("id", admin.ID).Info("默认管理员创建成功") + logrus.WithField("username", "admin").WithField("uuid", admin.UUID).Info("默认管理员创建成功") return nil } diff --git a/models/user.go b/models/user.go index b6895ff..016061b 100644 --- a/models/user.go +++ b/models/user.go @@ -1,12 +1,18 @@ package models -import "time" +import ( + "strings" + "time" + + "github.com/google/uuid" + "gorm.io/gorm" +) // User 用户表模型 // 说明:PasswordSalt 使用 32 字节随机盐(以 16 进制存储为 64 个字符),因此列长度设置为 64 - type User struct { ID uint `gorm:"primaryKey;comment:用户ID,自增主键"` + UUID string `gorm:"uniqueIndex;size:36;not null;comment:用户的唯一标识符" json:"uuid"` Username string `gorm:"uniqueIndex;size:64;not null;comment:用户名,唯一索引"` Password string `gorm:"size:255;not null;comment:密码哈希值"` PasswordSalt string `gorm:"size:64;not null;comment:密码加密盐值"` @@ -14,3 +20,12 @@ type User struct { CreatedAt time.Time `gorm:"comment:创建时间"` UpdatedAt time.Time `gorm:"comment:更新时间"` } + +// BeforeCreate 在创建记录前自动生成UUID +func (user *User) BeforeCreate(tx *gorm.DB) error { + // 生成UUID + if user.UUID == "" { + user.UUID = strings.ToUpper(uuid.New().String()) + } + return nil +} diff --git a/utils/crypto.go b/utils/crypto.go index 16daafd..71fec79 100644 --- a/utils/crypto.go +++ b/utils/crypto.go @@ -299,8 +299,8 @@ func HashPasswordWithSalt(password, salt string) (string, error) { // 将密码和盐值组合 combined := password + salt - // 使用bcrypt进行哈希(成本因子12,平衡安全性和性能) - hashed, err := bcrypt.GenerateFromPassword([]byte(combined), 12) + // 使用bcrypt进行哈希(成本因子10,平衡安全性和性能) + hashed, err := bcrypt.GenerateFromPassword([]byte(combined), 10) if err != nil { return "", err } @@ -321,3 +321,10 @@ func VerifyPasswordWithSalt(password, salt, hashedPassword string) bool { err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(combined)) return err == nil } + +// GenerateSHA256Hash 生成字符串的SHA256哈希值 +// 用于生成密码哈希摘要,用于JWT验证 +func GenerateSHA256Hash(input string) string { + hash := sha256.Sum256([]byte(input)) + return fmt.Sprintf("%x", hash) +} diff --git a/web/template/admin/apis.html b/web/template/admin/apis.html index 20dba0d..fbdc290 100644 --- a/web/template/admin/apis.html +++ b/web/template/admin/apis.html @@ -215,6 +215,7 @@ layui.use(['table', 'form', 'layer', 'dropdown'], function() { cols: [[ { field: 'id', title: 'ID', width: 80, sort: true }, { field: 'app_name', title: '应用名称', minWidth: 150 }, + { field: 'uuid', title: 'UUID', minWidth: 335 }, { field: 'api_type_name', title: '接口类型', minWidth: 120 }, { field: 'status_name', diff --git a/web/template/admin/user.html b/web/template/admin/user.html index 135b2ae..a37f83d 100644 --- a/web/template/admin/user.html +++ b/web/template/admin/user.html @@ -1,63 +1,251 @@ {{ define "user.html" }} -