mirror of
https://github.com/skyle1995/NetworkAuth.git
synced 2026-05-25 02:24:05 +08:00
283 lines
9.1 KiB
Go
283 lines
9.1 KiB
Go
package admin
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"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 查询当前登录管理员的基本信息
|
||
// - 返回 uuid/username/role/created_at 四个字段
|
||
// - 自动刷新接近过期的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
|
||
}
|
||
|
||
// 查询用户完整信息以获取创建时间
|
||
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{}{
|
||
"uuid": user.UUID,
|
||
"username": user.Username,
|
||
"role": user.Role,
|
||
"created_at": user.CreatedAt,
|
||
})
|
||
}
|
||
|
||
// 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.Where("uuid = ?", claims.UserUUID).First(&user).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 {
|
||
// 添加详细错误日志
|
||
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 dbErr := db.Model(&models.User{}).Where("uuid = ?", claims.UserUUID).Updates(map[string]interface{}{
|
||
"password": hash,
|
||
"password_salt": newSalt,
|
||
}).Error; dbErr != nil {
|
||
utils.JsonResponse(w, http.StatusInternalServerError, false, "更新密码失败", nil)
|
||
return
|
||
}
|
||
|
||
// 重新查询用户信息(包含新密码)
|
||
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 修改当前登录管理员的用户名
|
||
// - 接收 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
|
||
}
|
||
|
||
// 检查唯一性:排除当前用户UUID
|
||
var cnt int64
|
||
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
|
||
}
|
||
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.Where("uuid = ?", claims.UserUUID).First(&user).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("uuid = ?", claims.UserUUID).Update("username", username).Error; dbErr != nil {
|
||
utils.JsonResponse(w, http.StatusInternalServerError, false, "更新用户名失败", nil)
|
||
return
|
||
}
|
||
|
||
// 重新签发JWT并写入Cookie
|
||
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)
|
||
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,
|
||
})
|
||
}
|