Files
NetworkAuth/controllers/admin/user.go

239 lines
7.5 KiB
Go
Raw Normal View History

2025-10-24 00:09:45 +08:00
package admin
import (
"encoding/json"
"net/http"
"networkDev/database"
"networkDev/models"
"networkDev/utils"
"strings"
)
// UserFragmentHandler 个人资料片段渲染
// - 渲染个人资料与修改密码表单
func UserFragmentHandler(w http.ResponseWriter, r *http.Request) {
utils.RenderTemplate(w, "user.html", map[string]interface{}{})
}
// UserProfileQueryHandler 查询当前登录管理员的基本信息
// - 返回 id/username/role 三个字段
// - 自动刷新接近过期的JWT令牌
func UserProfileQueryHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
claims, _, err := GetCurrentAdminUserWithRefresh(w, r)
if err != nil {
utils.JsonResponse(w, http.StatusUnauthorized, false, "未登录或会话已过期", nil)
return
}
utils.JsonResponse(w, http.StatusOK, true, "ok", map[string]interface{}{
"id": claims.UserID,
"username": claims.Username,
"role": claims.Role,
})
}
// UserPasswordUpdateHandler 修改当前登录管理员的密码
// - 接收 JSON: {old_password, new_password, confirm_password}
// - 校验旧密码正确性、新密码与确认一致性
// - 成功后更新密码哈希
// - 自动刷新接近过期的JWT令牌
func UserPasswordUpdateHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
claims, _, err := GetCurrentAdminUserWithRefresh(w, r)
if err != nil {
utils.JsonResponse(w, http.StatusUnauthorized, false, "未登录或会话已过期", nil)
return
}
var body struct {
OldPassword string `json:"old_password"`
NewPassword string `json:"new_password"`
ConfirmPassword string `json:"confirm_password"`
}
var decodeErr error
if decodeErr = json.NewDecoder(r.Body).Decode(&body); decodeErr != nil {
utils.JsonResponse(w, http.StatusBadRequest, false, "请求参数错误", nil)
return
}
// 基础校验
if body.OldPassword == "" || body.NewPassword == "" || body.ConfirmPassword == "" {
utils.JsonResponse(w, http.StatusBadRequest, false, "旧密码/新密码/确认密码均不能为空", nil)
return
}
if len(body.NewPassword) < 6 {
utils.JsonResponse(w, http.StatusBadRequest, false, "新密码长度不能少于6位", nil)
return
}
if body.NewPassword != body.ConfirmPassword {
utils.JsonResponse(w, http.StatusBadRequest, false, "两次输入的新密码不一致", nil)
return
}
if body.NewPassword == body.OldPassword {
utils.JsonResponse(w, http.StatusBadRequest, false, "新密码不能与旧密码相同", nil)
return
}
db, err := database.GetDB()
if err != nil {
utils.JsonResponse(w, http.StatusInternalServerError, false, "数据库连接失败", nil)
return
}
// 查询当前用户
var user models.User
if dbErr := db.First(&user, claims.UserID).Error; dbErr != nil {
utils.JsonResponse(w, http.StatusNotFound, false, "用户不存在", nil)
return
}
// 校验旧密码(使用盐值验证)
if !utils.VerifyPasswordWithSalt(body.OldPassword, user.PasswordSalt, user.Password) {
utils.JsonResponse(w, http.StatusUnauthorized, false, "旧密码不正确", nil)
return
}
// 生成新的密码盐值
newSalt, err := utils.GenerateRandomSalt()
if err != nil {
utils.JsonResponse(w, http.StatusInternalServerError, false, "生成密码盐失败", nil)
return
}
// 使用新盐值生成密码哈希
hash, err := utils.HashPasswordWithSalt(body.NewPassword, newSalt)
if err != nil {
utils.JsonResponse(w, http.StatusInternalServerError, false, "生成密码哈希失败", nil)
return
}
// 更新密码和盐值
if err := db.Model(&models.User{}).Where("id = ?", claims.UserID).Updates(map[string]interface{}{
"password": hash,
"password_salt": newSalt,
}).Error; err != nil {
utils.JsonResponse(w, http.StatusInternalServerError, false, "更新密码失败", nil)
return
}
// 可选:安全起见,通知前端跳转到登录页
utils.JsonResponse(w, http.StatusOK, true, "密码修改成功,请重新登录", map[string]interface{}{
"redirect": "/admin/login",
})
}
// UserProfileUpdateHandler 修改当前登录管理员的用户名
// - 接收 JSON: {username}
// - 校验用户名非空、长度与唯一性
// - 更新数据库后重新签发JWT并写入 Cookie保持前端展示的一致性
// - 自动刷新接近过期的JWT令牌
func UserProfileUpdateHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
claims, _, err := GetCurrentAdminUserWithRefresh(w, r)
if err != nil {
utils.JsonResponse(w, http.StatusUnauthorized, false, "未登录或会话已过期", nil)
return
}
var body struct {
Username string `json:"username"`
OldPassword string `json:"old_password"`
}
if decodeErr := json.NewDecoder(r.Body).Decode(&body); decodeErr != nil {
utils.JsonResponse(w, http.StatusBadRequest, false, "请求参数错误", nil)
return
}
username := strings.TrimSpace(body.Username)
if username == "" {
utils.JsonResponse(w, http.StatusBadRequest, false, "用户名不能为空", nil)
return
}
if len(username) > 64 {
utils.JsonResponse(w, http.StatusBadRequest, false, "用户名长度不能超过64字符", nil)
return
}
db, err := database.GetDB()
if err != nil {
utils.JsonResponse(w, http.StatusInternalServerError, false, "数据库连接失败", nil)
return
}
// 检查唯一性排除当前用户ID
var cnt int64
if dbErr := db.Model(&models.User{}).Where("username = ? AND id <> ?", username, claims.UserID).Count(&cnt).Error; dbErr != nil {
utils.JsonResponse(w, http.StatusInternalServerError, false, "检查用户名唯一性失败", nil)
return
}
if cnt > 0 {
utils.JsonResponse(w, http.StatusBadRequest, false, "用户名已存在,请更换", nil)
return
}
// 如果未变化则直接返回成功(无需校验旧密码)
if strings.EqualFold(username, claims.Username) {
utils.JsonResponse(w, http.StatusOK, true, "保存成功", map[string]interface{}{
"username": username,
})
return
}
// 修改用户名需要进行当前密码校验
if strings.TrimSpace(body.OldPassword) == "" {
utils.JsonResponse(w, http.StatusBadRequest, false, "修改用户名需要提供当前密码", nil)
return
}
// 查询当前用户并校验旧密码
var user models.User
if dbErr := db.First(&user, claims.UserID).Error; dbErr != nil {
utils.JsonResponse(w, http.StatusNotFound, false, "用户不存在", nil)
return
}
// 使用盐值验证当前密码
if !utils.VerifyPasswordWithSalt(body.OldPassword, user.PasswordSalt, user.Password) {
utils.JsonResponse(w, http.StatusUnauthorized, false, "当前密码不正确", nil)
return
}
// 执行更新
if dbErr := db.Model(&models.User{}).Where("id = ?", claims.UserID).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}
token, err := generateJWTToken(newUser)
if err != nil {
utils.JsonResponse(w, http.StatusInternalServerError, false, "生成新令牌失败", nil)
return
}
cookie := &http.Cookie{
Name: "admin_session",
Value: token,
Path: "/",
HttpOnly: true,
Secure: false,
MaxAge: 24 * 60 * 60,
}
http.SetCookie(w, cookie)
utils.JsonResponse(w, http.StatusOK, true, "保存成功", map[string]interface{}{
"username": username,
})
}