server: 平台抽象层+bilibili、服务端加密凭据库、角色中间件;web: 精简页面/路由、macOS 客户端(Swift)与多份方案文档;移除误入库的编译产物
This commit is contained in:
@@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -42,6 +43,11 @@ type logoutReq struct {
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
}
|
||||
|
||||
type changePasswordReq struct {
|
||||
CurrentPassword string `json:"currentPassword" binding:"required"`
|
||||
NewPassword string `json:"newPassword" binding:"required,min=8"`
|
||||
}
|
||||
|
||||
// Register 注册:创建用户 + 默认工作空间 + owner 成员
|
||||
func (h *AuthHandler) Register(c *gin.Context) {
|
||||
var req registerReq
|
||||
@@ -177,8 +183,13 @@ func (h *AuthHandler) Refresh(c *gin.Context) {
|
||||
response.Fail(c, http.StatusUnauthorized, 2003, "用户不存在")
|
||||
return
|
||||
}
|
||||
workspaceID := claims.WorkspaceID
|
||||
var member models.Member
|
||||
if err = h.DB.Where("user_id = ?", user.ID).Order("id asc").First(&member).Error; err != nil {
|
||||
memberQuery := h.DB.Where("user_id = ?", user.ID)
|
||||
if workspaceID != 0 {
|
||||
memberQuery = memberQuery.Where("workspace_id = ?", workspaceID)
|
||||
}
|
||||
if err = memberQuery.Order("id asc").First(&member).Error; err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 5000, "工作空间数据缺失")
|
||||
return
|
||||
}
|
||||
@@ -203,6 +214,39 @@ func (h *AuthHandler) Logout(c *gin.Context) {
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) ChangePassword(c *gin.Context) {
|
||||
var req changePasswordReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 1001, "密码格式不合法")
|
||||
return
|
||||
}
|
||||
var user models.User
|
||||
if err := h.DB.First(&user, c.GetUint64("uid")).Error; err != nil {
|
||||
response.Fail(c, http.StatusUnauthorized, 1002, "用户不存在")
|
||||
return
|
||||
}
|
||||
ok, err := auth.VerifyPassword(req.CurrentPassword, user.PasswordHash)
|
||||
if err != nil || !ok {
|
||||
response.Fail(c, http.StatusBadRequest, 2002, "当前密码错误")
|
||||
return
|
||||
}
|
||||
if req.CurrentPassword == req.NewPassword {
|
||||
response.Fail(c, http.StatusBadRequest, 1001, "新密码不能与当前密码相同")
|
||||
return
|
||||
}
|
||||
hash, err := auth.HashPassword(req.NewPassword)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 5000, "密码更新失败")
|
||||
return
|
||||
}
|
||||
if err = h.DB.Model(&user).Update("password_hash", hash).Error; err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 5000, "密码更新失败")
|
||||
return
|
||||
}
|
||||
response.Audit(c, "auth.password_change", "user:"+itoa(user.ID), nil)
|
||||
response.OK(c, nil)
|
||||
}
|
||||
|
||||
// Me 当前用户信息
|
||||
func (h *AuthHandler) Me(c *gin.Context) {
|
||||
var user models.User
|
||||
@@ -213,6 +257,34 @@ func (h *AuthHandler) Me(c *gin.Context) {
|
||||
response.OK(c, gin.H{"user": userDTO(user), "workspaceId": c.GetUint64("wsid"), "memberRole": c.GetString("mrole")})
|
||||
}
|
||||
|
||||
// SwitchWorkspace issues a fresh access/refresh pair for another workspace
|
||||
// where the current user is a member. The old token remains valid until its
|
||||
// normal expiry, but all resource handlers use the newly selected workspace.
|
||||
func (h *AuthHandler) SwitchWorkspace(c *gin.Context) {
|
||||
wsid, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 1001, "参数不合法")
|
||||
return
|
||||
}
|
||||
var member models.Member
|
||||
if err = h.DB.Where("workspace_id = ? and user_id = ?", wsid, c.GetUint64("uid")).First(&member).Error; err != nil {
|
||||
response.Fail(c, http.StatusForbidden, 1003, "不是该工作空间成员")
|
||||
return
|
||||
}
|
||||
var user models.User
|
||||
if err = h.DB.First(&user, c.GetUint64("uid")).Error; err != nil || user.Status != "active" {
|
||||
response.Fail(c, http.StatusUnauthorized, 1002, "用户不可用")
|
||||
return
|
||||
}
|
||||
access, refresh, err := h.issuePair(user.ID, wsid, user.Role, member.Role)
|
||||
if err != nil {
|
||||
response.Fail(c, http.StatusInternalServerError, 5000, "签发令牌失败")
|
||||
return
|
||||
}
|
||||
response.Audit(c, "workspace.switch", "workspace:"+itoa(wsid), nil)
|
||||
response.OK(c, gin.H{"accessToken": access, "refreshToken": refresh, "workspace": gin.H{"id": wsid}, "memberRole": member.Role})
|
||||
}
|
||||
|
||||
// issuePair 签发令牌对并把 refresh jti 登记到 Redis
|
||||
func (h *AuthHandler) issuePair(uid, wsid uint64, role, mrole string) (string, string, error) {
|
||||
access, err := auth.IssueAccess(h.Cfg.JWTSecret, uid, wsid, role, mrole)
|
||||
@@ -220,7 +292,7 @@ func (h *AuthHandler) issuePair(uid, wsid uint64, role, mrole string) (string, s
|
||||
return "", "", err
|
||||
}
|
||||
jti := uuid.NewString()
|
||||
refresh, err := auth.IssueRefresh(h.Cfg.JWTSecret, uid, jti)
|
||||
refresh, err := auth.IssueRefresh(h.Cfg.JWTSecret, uid, wsid, jti)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user