fix(account): 将删除账号接口从GET改为POST方法
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 6m55s
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 6m55s
修复删除账号接口的安全问题,GET方法不应用于敏感操作 同时增加邮箱验证码校验,提高账号安全性 ``` ```msg feat(auth): 在设备登录时更新用户代理信息 添加设备登录时更新用户代理(UA)的逻辑 确保设备信息保持最新状态 ``` ```msg refactor(handler): 重构删除账号处理器的验证逻辑 将邮箱验证码校验逻辑提取为独立函数 提高代码可维护性和复用性
This commit is contained in:
parent
b1e9382e73
commit
ccdcfd3430
@ -1,17 +1,39 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/perfect-panel/server/internal/logic/public/user"
|
"github.com/perfect-panel/server/internal/logic/public/user"
|
||||||
"github.com/perfect-panel/server/internal/svc"
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/pkg/constant"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeleteAccountHandler 注销账号处理器
|
// DeleteAccountHandler 注销账号处理器
|
||||||
// 根据当前token删除所有关联设备,然后根据各自设备ID重新新建账号
|
// 根据当前token删除所有关联设备,然后根据各自设备ID重新新建账号
|
||||||
|
// 新增:需携带邮箱验证码,验证通过后执行注销
|
||||||
|
type deleteAccountReq struct {
|
||||||
|
EmailCode string `json:"email_code" binding:"required"` // 邮箱验证码
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteAccountHandler(serverCtx *svc.ServiceContext) gin.HandlerFunc {
|
func DeleteAccountHandler(serverCtx *svc.ServiceContext) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
|
var req deleteAccountReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验邮箱验证码
|
||||||
|
if err := verifyEmailCode(c.Request.Context(), serverCtx, req.EmailCode); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
l := user.NewDeleteAccountLogic(c.Request.Context(), serverCtx)
|
l := user.NewDeleteAccountLogic(c.Request.Context(), serverCtx)
|
||||||
resp, err := l.DeleteAccount()
|
resp, err := l.DeleteAccount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -21,3 +43,27 @@ func DeleteAccountHandler(serverCtx *svc.ServiceContext) gin.HandlerFunc {
|
|||||||
c.JSON(http.StatusOK, resp)
|
c.JSON(http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CacheKeyPayload struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
LastAt int64 `json:"lastAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyEmailCode(ctx context.Context, serverCtx *svc.ServiceContext, code string) error {
|
||||||
|
userEmail := ctx.Value("user_email").(string)
|
||||||
|
cacheKey := fmt.Sprintf("auth_code:%s:%s", constant.Security, userEmail)
|
||||||
|
val, err := serverCtx.Redis.Get(ctx, cacheKey).Result()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to get cached code")
|
||||||
|
}
|
||||||
|
var payload CacheKeyPayload
|
||||||
|
if err := json.Unmarshal([]byte(val), &payload); err != nil {
|
||||||
|
return errors.Wrap(err, "invalid cached payload")
|
||||||
|
}
|
||||||
|
if payload.Code != code {
|
||||||
|
return errors.New("invalid email code")
|
||||||
|
}
|
||||||
|
// 验证通过,立即删除缓存,防止重复使用
|
||||||
|
_ = serverCtx.Redis.Del(ctx, cacheKey)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -775,7 +775,7 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
|
|||||||
publicUserGroupRouter.GET("/affiliate/list", publicUser.QueryUserAffiliateListHandler(serverCtx))
|
publicUserGroupRouter.GET("/affiliate/list", publicUser.QueryUserAffiliateListHandler(serverCtx))
|
||||||
|
|
||||||
// Delete Account
|
// Delete Account
|
||||||
publicUserGroupRouter.GET("/delete_account", publicUser.DeleteAccountHandler(serverCtx))
|
publicUserGroupRouter.POST("/delete_account", publicUser.DeleteAccountHandler(serverCtx))
|
||||||
|
|
||||||
// Query User Balance Log
|
// Query User Balance Log
|
||||||
publicUserGroupRouter.GET("/balance_log", publicUser.QueryUserBalanceLogHandler(serverCtx))
|
publicUserGroupRouter.GET("/balance_log", publicUser.QueryUserBalanceLogHandler(serverCtx))
|
||||||
|
|||||||
@ -43,6 +43,7 @@ func (l *DeviceLoginLogic) DeviceLogin(req *types.DeviceLoginRequest) (resp *typ
|
|||||||
var userInfo *user.User
|
var userInfo *user.User
|
||||||
// Record login status
|
// Record login status
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
||||||
if userInfo != nil && userInfo.Id != 0 {
|
if userInfo != nil && userInfo.Id != 0 {
|
||||||
loginLog := log.Login{
|
loginLog := log.Login{
|
||||||
Method: "device",
|
Method: "device",
|
||||||
@ -95,6 +96,16 @@ func (l *DeviceLoginLogic) DeviceLogin(req *types.DeviceLoginRequest) (resp *typ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据 req 中的UA 更新UA
|
||||||
|
deviceInfo.UserAgent = req.UserAgent
|
||||||
|
if err := l.svcCtx.UserModel.UpdateDevice(l.ctx, deviceInfo); err != nil {
|
||||||
|
l.Errorw("update user agent failed",
|
||||||
|
logger.Field("device_id", deviceInfo.Id),
|
||||||
|
logger.Field("error", err.Error()),
|
||||||
|
)
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update user agent failed: %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
// Generate session id
|
// Generate session id
|
||||||
sessionId := uuidx.NewUUID().String()
|
sessionId := uuidx.NewUUID().String()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user