⚡ login performance optimization
This commit is contained in:
@@ -30,32 +30,37 @@ func (CaptchaAPI) GenerateRotateCaptcha(c *gin.Context) {
|
|||||||
captchaData, err := global.RotateCaptcha.Generate()
|
captchaData, err := global.RotateCaptcha.Generate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Fatalln(err)
|
global.LOG.Fatalln(err)
|
||||||
|
result.FailWithNull(c)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
blockData := captchaData.GetData()
|
blockData := captchaData.GetData()
|
||||||
if blockData == nil {
|
if blockData == nil {
|
||||||
result.FailWithNull(c)
|
result.FailWithNull(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var masterImageBase64, thumbImageBase64 string
|
|
||||||
masterImageBase64 = captchaData.GetMasterImage().ToBase64()
|
masterImageBase64 := captchaData.GetMasterImage().ToBase64()
|
||||||
thumbImageBase64 = captchaData.GetThumbImage().ToBase64()
|
thumbImageBase64 := captchaData.GetThumbImage().ToBase64()
|
||||||
dotsByte, err := json.Marshal(blockData)
|
dotsByte, err := json.Marshal(blockData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
global.LOG.Fatalln(err)
|
||||||
result.FailWithNull(c)
|
result.FailWithNull(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
key := helper.StringToMD5(string(dotsByte))
|
key := helper.StringToMD5(string(dotsByte))
|
||||||
err = redis.Set(constant.UserLoginCaptchaRedisKey+key, dotsByte, time.Minute).Err()
|
err = redis.Set(constant.UserLoginCaptchaRedisKey+key, dotsByte, time.Minute).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
global.LOG.Fatalln(err)
|
||||||
result.FailWithNull(c)
|
result.FailWithNull(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bt := map[string]interface{}{
|
|
||||||
|
result.OkWithData(map[string]interface{}{
|
||||||
"key": key,
|
"key": key,
|
||||||
"image": masterImageBase64,
|
"image": masterImageBase64,
|
||||||
"thumb": thumbImageBase64,
|
"thumb": thumbImageBase64,
|
||||||
}
|
}, c)
|
||||||
result.OkWithData(bt, c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckRotateData 验证旋转验证码
|
// CheckRotateData 验证旋转验证码
|
||||||
@@ -67,30 +72,36 @@ func (CaptchaAPI) GenerateRotateCaptcha(c *gin.Context) {
|
|||||||
// @Success 200 {string} json
|
// @Success 200 {string} json
|
||||||
// @Router /api/captcha/rotate/check [post]
|
// @Router /api/captcha/rotate/check [post]
|
||||||
func (CaptchaAPI) CheckRotateData(c *gin.Context) {
|
func (CaptchaAPI) CheckRotateData(c *gin.Context) {
|
||||||
rotateRequest := dto.RotateCaptchaRequest{}
|
var rotateRequest dto.RotateCaptchaRequest
|
||||||
err := c.ShouldBindJSON(&rotateRequest)
|
if err := c.ShouldBindJSON(&rotateRequest); err != nil {
|
||||||
angle := rotateRequest.Angle
|
|
||||||
key := rotateRequest.Key
|
|
||||||
if err != nil {
|
|
||||||
result.FailWithNull(c)
|
result.FailWithNull(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cacheDataByte, err := redis.Get(constant.UserLoginCaptchaRedisKey + key).Bytes()
|
|
||||||
if len(cacheDataByte) == 0 || err != nil {
|
cacheDataByte, err := redis.Get(constant.UserLoginCaptchaRedisKey + rotateRequest.Key).Bytes()
|
||||||
|
if err != nil || len(cacheDataByte) == 0 {
|
||||||
result.FailWithCodeAndMessage(1011, ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
result.FailWithCodeAndMessage(1011, ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var dct *rotate.Block
|
var dct *rotate.Block
|
||||||
if err := json.Unmarshal(cacheDataByte, &dct); err != nil {
|
if err := json.Unmarshal(cacheDataByte, &dct); err != nil {
|
||||||
result.FailWithNull(c)
|
result.FailWithNull(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sAngle, _ := strconv.ParseFloat(fmt.Sprintf("%v", angle), 64)
|
|
||||||
|
sAngle, err := strconv.ParseFloat(fmt.Sprintf("%v", rotateRequest.Angle), 64)
|
||||||
|
if err != nil {
|
||||||
|
result.FailWithNull(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
chkRet := rotate.CheckAngle(int64(sAngle), int64(dct.Angle), 2)
|
chkRet := rotate.CheckAngle(int64(sAngle), int64(dct.Angle), 2)
|
||||||
if chkRet {
|
if chkRet {
|
||||||
result.OkWithMessage("success", c)
|
result.OkWithMessage("success", c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result.FailWithMessage("fail", c)
|
result.FailWithMessage("fail", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -40,13 +40,7 @@ var mu sync.Mutex
|
|||||||
// @Router /api/oauth/generate_client_id [get]
|
// @Router /api/oauth/generate_client_id [get]
|
||||||
func (OAuthAPI) GenerateClientId(c *gin.Context) {
|
func (OAuthAPI) GenerateClientId(c *gin.Context) {
|
||||||
// 获取客户端IP
|
// 获取客户端IP
|
||||||
ip := c.GetHeader("X-Real-IP")
|
ip := utils.GetClientIP(c)
|
||||||
if ip == "" {
|
|
||||||
ip = c.GetHeader("X-Forwarded-For")
|
|
||||||
}
|
|
||||||
if ip == "" {
|
|
||||||
ip = c.ClientIP()
|
|
||||||
}
|
|
||||||
// 加锁
|
// 加锁
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
@@ -161,49 +155,48 @@ func (OAuthAPI) CallbackVerify(c *gin.Context) {
|
|||||||
// @Router /api/oauth/get_temp_qrcode [get]
|
// @Router /api/oauth/get_temp_qrcode [get]
|
||||||
func (OAuthAPI) GetTempQrCode(c *gin.Context) {
|
func (OAuthAPI) GetTempQrCode(c *gin.Context) {
|
||||||
clientId := c.Query("client_id")
|
clientId := c.Query("client_id")
|
||||||
// 获取客户端IP
|
|
||||||
ip := c.GetHeader("X-Real-IP")
|
|
||||||
if ip == "" {
|
|
||||||
ip = c.GetHeader("X-Forwarded-For")
|
|
||||||
}
|
|
||||||
if ip == "" {
|
|
||||||
ip = c.ClientIP()
|
|
||||||
}
|
|
||||||
if clientId == "" {
|
if clientId == "" {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
qrcode := redis.Get(constant.UserLoginQrcodeRedisKey + ip + ":" + clientId).Val()
|
|
||||||
|
|
||||||
|
ip := utils.GetClientIP(c) // 使用工具函数获取客户端IP
|
||||||
|
key := constant.UserLoginQrcodeRedisKey + ip + ":" + clientId
|
||||||
|
|
||||||
|
// 从Redis获取二维码数据
|
||||||
|
qrcode := redis.Get(key).Val()
|
||||||
if qrcode != "" {
|
if qrcode != "" {
|
||||||
data := response.ResponseQRCodeCreate{}
|
data := new(response.ResponseQRCodeCreate)
|
||||||
err := json.Unmarshal([]byte(qrcode), &data)
|
if err := json.Unmarshal([]byte(qrcode), data); err != nil {
|
||||||
if err != nil {
|
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c)
|
result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成临时二维码
|
||||||
data, err := global.Wechat.QRCode.Temporary(c.Request.Context(), clientId, 30*24*3600)
|
data, err := global.Wechat.QRCode.Temporary(c.Request.Context(), clientId, 30*24*3600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 序列化数据并存储到Redis
|
||||||
serializedData, err := json.Marshal(data)
|
serializedData, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wrong := redis.Set(constant.UserLoginQrcodeRedisKey+ip+":"+clientId, serializedData, time.Hour*24*30).Err()
|
if err := redis.Set(key, serializedData, time.Hour*24*30).Err(); err != nil {
|
||||||
|
global.LOG.Error(err)
|
||||||
if wrong != nil {
|
|
||||||
global.LOG.Error(wrong)
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "QRCodeGetFailed"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c)
|
result.OK(ginI18n.MustGetMessage(c, "QRCodeGetSuccess"), data.Url, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -98,58 +98,6 @@ func (UserAPI) QueryUserByPhone(c *gin.Context) {
|
|||||||
result.OkWithData(user, c)
|
result.OkWithData(user, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddUser 添加用户
|
|
||||||
// @Summary 添加用户
|
|
||||||
// @Tags 用户模块
|
|
||||||
// @Param user body dto.AddUserRequest true "用户信息"
|
|
||||||
// @Success 200 {string} json
|
|
||||||
// @Router /api/user/add [post]
|
|
||||||
func (UserAPI) AddUser(c *gin.Context) {
|
|
||||||
addUserRequest := dto.AddUserRequest{}
|
|
||||||
err := c.ShouldBindJSON(&addUserRequest)
|
|
||||||
if err != nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
username := userService.QueryUserByUsername(addUserRequest.Username)
|
|
||||||
if !reflect.DeepEqual(username, model.ScaAuthUser{}) {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "UsernameExists"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
phone := userService.QueryUserByPhone(addUserRequest.Phone)
|
|
||||||
if !reflect.DeepEqual(phone, model.ScaAuthUser{}) {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneExists"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
encrypt, err := utils.Encrypt(addUserRequest.Password)
|
|
||||||
if err != nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "AddUserError"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uid := idgen.NextId()
|
|
||||||
uidStr := strconv.FormatInt(uid, 10)
|
|
||||||
user := model.ScaAuthUser{
|
|
||||||
UID: &uidStr,
|
|
||||||
Username: &addUserRequest.Username,
|
|
||||||
Password: &encrypt,
|
|
||||||
Phone: &addUserRequest.Phone,
|
|
||||||
}
|
|
||||||
_, err = userService.AddUser(user)
|
|
||||||
if err != nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "AddUserError"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = global.Casbin.AddRoleForUser(uidStr, enum.User)
|
|
||||||
if err != nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "AddUserRoleError"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "AddUserSuccess"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// AccountLogin 账号登录
|
// AccountLogin 账号登录
|
||||||
// @Summary 账号登录
|
// @Summary 账号登录
|
||||||
// @Tags 用户模块
|
// @Tags 用户模块
|
||||||
@@ -219,30 +167,11 @@ func (UserAPI) PhoneLogin(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 异步查询用户信息
|
|
||||||
userChan := make(chan *model.ScaAuthUser)
|
|
||||||
go func() {
|
|
||||||
user := userService.QueryUserByPhone(phone)
|
user := userService.QueryUserByPhone(phone)
|
||||||
userChan <- &user
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 异步获取验证码
|
|
||||||
codeChan := make(chan string)
|
|
||||||
go func() {
|
|
||||||
code := redis.Get(constant.UserLoginSmsRedisKey + phone)
|
|
||||||
if code == nil {
|
|
||||||
codeChan <- ""
|
|
||||||
} else {
|
|
||||||
codeChan <- code.Val()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
user := <-userChan
|
|
||||||
code := <-codeChan
|
|
||||||
|
|
||||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||||
// 未注册
|
// 未注册
|
||||||
if code == "" {
|
code := redis.Get(constant.UserLoginSmsRedisKey + phone)
|
||||||
|
if code == nil {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
@@ -263,23 +192,31 @@ func (UserAPI) PhoneLogin(c *gin.Context) {
|
|||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "RegisterUserError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "RegisterUserError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
err = global.Casbin.SavePolicy()
|
||||||
|
if err != nil {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "RegisterUserError"), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
handelUserLogin(addUser, request.AutoLogin, c)
|
handelUserLogin(addUser, request.AutoLogin, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if code == "" {
|
code := redis.Get(constant.UserLoginSmsRedisKey + phone)
|
||||||
|
if code == nil {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
if captcha != code {
|
if captcha != code.Val() {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
handelUserLogin(*user, request.AutoLogin, c)
|
handelUserLogin(user, request.AutoLogin, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshHandler 刷新token
|
// RefreshHandler 刷新token
|
||||||
@@ -300,15 +237,11 @@ func (UserAPI) RefreshHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
parseRefreshToken, isUpd, err := utils.ParseRefreshToken(refreshToken)
|
parseRefreshToken, isUpd, err := utils.ParseRefreshToken(refreshToken)
|
||||||
if err != nil {
|
if err != nil || !isUpd {
|
||||||
global.LOG.Errorln(err)
|
global.LOG.Errorln(err)
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !isUpd {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
accessTokenString, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: parseRefreshToken.UserID})
|
accessTokenString, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: parseRefreshToken.UserID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
||||||
@@ -316,7 +249,7 @@ func (UserAPI) RefreshHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
tokenKey := constant.UserLoginTokenRedisKey + *parseRefreshToken.UserID
|
tokenKey := constant.UserLoginTokenRedisKey + *parseRefreshToken.UserID
|
||||||
token, err := redis.Get(tokenKey).Result()
|
token, err := redis.Get(tokenKey).Result()
|
||||||
if token == "" || err != nil {
|
if err != nil || token == "" {
|
||||||
global.LOG.Errorln(err)
|
global.LOG.Errorln(err)
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c)
|
||||||
return
|
return
|
||||||
@@ -335,17 +268,24 @@ func (UserAPI) RefreshHandler(c *gin.Context) {
|
|||||||
|
|
||||||
// handelUserLogin 处理用户登录
|
// handelUserLogin 处理用户登录
|
||||||
func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
|
func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
|
||||||
|
// 检查 user.UID 是否为 nil
|
||||||
|
if user.UID == nil {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: user.UID})
|
accessToken, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: user.UID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var days time.Duration
|
var days time.Duration
|
||||||
if autoLogin {
|
if autoLogin {
|
||||||
days = time.Hour * 24 * 7
|
days = 7 * 24 * time.Hour
|
||||||
} else {
|
} else {
|
||||||
days = time.Hour * 24 * 1
|
days = 24 * time.Hour
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID}, days)
|
refreshToken, expiresAt := utils.GenerateRefreshToken(utils.RefreshJWTPayload{UserID: user.UID}, days)
|
||||||
data := dto.ResponseData{
|
data := dto.ResponseData{
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
@@ -353,13 +293,14 @@ func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
|
|||||||
ExpiresAt: expiresAt,
|
ExpiresAt: expiresAt,
|
||||||
UID: user.UID,
|
UID: user.UID,
|
||||||
}
|
}
|
||||||
fail := redis.Set(constant.UserLoginTokenRedisKey+*user.UID, data, time.Hour*24*1).Err()
|
|
||||||
if fail != nil {
|
err = redis.Set(constant.UserLoginTokenRedisKey+*user.UID, data, days).Err()
|
||||||
|
if err != nil {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginFailed"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result.OkWithData(data, c)
|
result.OkWithData(data, c)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetPassword 重置密码
|
// ResetPassword 重置密码
|
||||||
@@ -369,50 +310,86 @@ func handelUserLogin(user model.ScaAuthUser, autoLogin bool, c *gin.Context) {
|
|||||||
// @Success 200 {string} json
|
// @Success 200 {string} json
|
||||||
// @Router /api/user/reset_password [post]
|
// @Router /api/user/reset_password [post]
|
||||||
func (UserAPI) ResetPassword(c *gin.Context) {
|
func (UserAPI) ResetPassword(c *gin.Context) {
|
||||||
resetPasswordRequest := dto.ResetPasswordRequest{}
|
var resetPasswordRequest dto.ResetPasswordRequest
|
||||||
err := c.ShouldBindJSON(&resetPasswordRequest)
|
if err := c.ShouldBindJSON(&resetPasswordRequest); err != nil {
|
||||||
if err != nil {
|
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
phone := resetPasswordRequest.Phone
|
phone := resetPasswordRequest.Phone
|
||||||
captcha := resetPasswordRequest.Captcha
|
captcha := resetPasswordRequest.Captcha
|
||||||
password := resetPasswordRequest.Password
|
password := resetPasswordRequest.Password
|
||||||
repassword := resetPasswordRequest.Repassword
|
repassword := resetPasswordRequest.Repassword
|
||||||
|
|
||||||
if phone == "" || captcha == "" || password == "" || repassword == "" {
|
if phone == "" || captcha == "" || password == "" || repassword == "" {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isPhone := utils.IsPhone(phone)
|
|
||||||
if !isPhone {
|
if !utils.IsPhone(phone) {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneErrorFormat"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
code := redis.Get(constant.UserLoginSmsRedisKey + phone)
|
|
||||||
if code == nil {
|
if password != repassword {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordNotSame"), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.IsPassword(password) {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用事务确保验证码检查和密码更新的原子性
|
||||||
|
tx := global.DB.Begin()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := tx.Error; err != nil {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "DatabaseError"), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code := redis.Get(constant.UserLoginSmsRedisKey + phone).Val()
|
||||||
|
if code == "" {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c)
|
||||||
return
|
return
|
||||||
} else {
|
}
|
||||||
if captcha != code.Val() {
|
|
||||||
|
if captcha != code {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证码检查通过后立即删除或标记为已使用
|
||||||
|
if err := redis.Del(constant.UserLoginSmsRedisKey + phone).Err(); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError"), c)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user := userService.QueryUserByPhone(phone)
|
user := userService.QueryUserByPhone(phone)
|
||||||
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
if reflect.DeepEqual(user, model.ScaAuthUser{}) {
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotRegister"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotRegister"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
encrypt, err := utils.Encrypt(password)
|
encrypt, err := utils.Encrypt(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError")+": "+err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := userService.UpdateUser(phone, encrypt); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError"), c)
|
result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError"), c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wrong := userService.UpdateUser(phone, encrypt)
|
|
||||||
if wrong != nil {
|
tx.Commit()
|
||||||
result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError"), c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result.OkWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordSuccess"), c)
|
result.OkWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordSuccess"), c)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
@@ -52,3 +52,4 @@ ResetPasswordSuccess = "reset password success!"
|
|||||||
QRCodeGetFailed = "qr code get failed!"
|
QRCodeGetFailed = "qr code get failed!"
|
||||||
QRCodeGetSuccess = "qr code get successfully!"
|
QRCodeGetSuccess = "qr code get successfully!"
|
||||||
QRCodeExpired = "qr code expired!"
|
QRCodeExpired = "qr code expired!"
|
||||||
|
InternalError = "internal error!"
|
||||||
|
@@ -52,4 +52,5 @@ ResetPasswordSuccess = "重置密码成功!"
|
|||||||
QRCodeGetFailed = "获取二维码失败!"
|
QRCodeGetFailed = "获取二维码失败!"
|
||||||
QRCodeGetSuccess = "获取二维码成功!"
|
QRCodeGetSuccess = "获取二维码成功!"
|
||||||
QRCodeExpired = "二维码已过期!"
|
QRCodeExpired = "二维码已过期!"
|
||||||
|
InternalError = "内部错误!"
|
||||||
|
|
||||||
|
@@ -14,7 +14,6 @@ func UserRouter(router *gin.RouterGroup) {
|
|||||||
{
|
{
|
||||||
userGroup.POST("/login", userApi.AccountLogin)
|
userGroup.POST("/login", userApi.AccountLogin)
|
||||||
userGroup.POST("/phone_login", userApi.PhoneLogin)
|
userGroup.POST("/phone_login", userApi.PhoneLogin)
|
||||||
userGroup.POST("/add", userApi.AddUser)
|
|
||||||
userGroup.POST("/reset_password", userApi.ResetPassword)
|
userGroup.POST("/reset_password", userApi.ResetPassword)
|
||||||
}
|
}
|
||||||
authGroup := router.Group("auth").Use(middleware.JWTAuthMiddleware()).Use(middleware.CasbinMiddleware())
|
authGroup := router.Group("auth").Use(middleware.JWTAuthMiddleware()).Use(middleware.CasbinMiddleware())
|
||||||
|
15
utils/get_ip.go
Normal file
15
utils/get_ip.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
// GetClientIP 工具函数,获取客户端IP
|
||||||
|
func GetClientIP(c *gin.Context) string {
|
||||||
|
ip := c.GetHeader("X-Real-IP")
|
||||||
|
if ip == "" {
|
||||||
|
ip = c.GetHeader("X-Forwarded-For")
|
||||||
|
}
|
||||||
|
if ip == "" {
|
||||||
|
ip = c.ClientIP()
|
||||||
|
}
|
||||||
|
return ip
|
||||||
|
}
|
@@ -30,3 +30,10 @@ func IsUsername(username string) bool {
|
|||||||
match, _ := regexp.MatchString(phoneRegex, username)
|
match, _ := regexp.MatchString(phoneRegex, username)
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPassword 密码的正则表达式
|
||||||
|
func IsPassword(password string) bool {
|
||||||
|
phoneRegex := `^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{6,18}$`
|
||||||
|
match, _ := regexp.MatchString(phoneRegex, password)
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user