Files
full/module/base/mgt/internal/logic/pub/login.go
2026-09-22 21:15:34 +08:00

146 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package pub
import (
"errors"
"fmt"
"strconv"
"bsm/full/module/base/mgt/internal/impl"
"bsm/full/module/base/mgt/internal/models"
"bsm/full/module/base/mgt/internal/types"
"git.apinb.com/bsm-sdk/core/crypto/token"
"git.apinb.com/bsm-sdk/core/env"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/infra"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/vars"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// Login 登录
// @Summary 登录
// @Description 登录
// @Tags 登录
// @Accept application/json
// @Produce application/json
// @Param role body types.LoginRequest true "登录请求"
// @Security ApiKeyAuth
// @Success 200 {object} types.LoginResp "新增成功,返回token"
// @Router /login [post]
func Login(c *gin.Context) {
var (
request types.LoginRequest
resp = types.LoginResp{}
user = models.MgtUser{}
extend = map[string]string{}
err error
account string
pwd string
)
err = c.ShouldBindJSON(&request)
if err != nil {
infra.Response.Error(c, errcode.ErrJsonUnmarshal)
return
}
if request.Account != "" && request.Password != "" {
account = request.Account
pwd = request.Password
} else if request.AppId != "" && request.AppKey != "" {
account = request.AppId
pwd = request.AppKey
} else {
printer.Error("登录参数异常: 账号密码或AppId/AppKey必须提供")
infra.Response.Error(c, errcode.ErrInvalidArgument)
return
}
// 获取用户信息
if err = impl.DBService.Model(&models.MgtUser{}).Where("account=?", account).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
infra.Response.Error(c, errcode.ErrRecordNotFound)
return
}
infra.Response.Error(c, errcode.ErrDB)
return
}
resp = types.LoginResp{Phone: user.Phone, Email: user.Email, Name: user.Name, UserId: user.ID, Account: user.Account, Avatar: user.Avatar, Identity: user.Identity}
// 检测Password与Status
if request.AppKey != "" {
err = checkAppKeyAndStatus(user.Password, pwd, user.Salt, int16(user.Status))
} else {
err = checkPwdAndStatus(user.Password, pwd, user.Salt, int16(user.Status))
}
if err != nil {
printer.Error("检测Password与Status异常: %v", err)
infra.Response.Error(c, err)
return
}
extend["id"] = strconv.Itoa(int(user.ID))
extend["Identity"] = user.Identity
extend["status"] = strconv.Itoa(int(user.Status))
extend["name"] = user.Name
// 获取token
resp.Token, err = token.New(env.Runtime.JwtSecretKey).GenerateJwt(uint(user.ID), user.Identity, c.ClientIP(), "", nil, extend)
if err != nil {
printer.Error("获取token异常: %v", err)
infra.Response.Error(c, err)
return
}
err = models.UpdateLastLoginDate(uint(user.ID))
if err != nil {
printer.Error("更新最后登录时间异常: %v", err)
infra.Response.Error(c, errcode.ErrInternal)
return
}
printer.Info("用户登录成功: account=%s, userId=%d", account, user.ID)
infra.Response.Success(c, resp)
}
func SaveToken(id int64, role, token string) error {
tokenKey := fmt.Sprintf("%d-%s-%s", uint(id), role, "token")
status := impl.RedisService.Client.Set(impl.RedisService.Ctx, tokenKey, token, 0)
if status.Val() != "OK" {
printer.Error("保存token失败: %v", errcode.ErrTokenAuthTokenChanged)
return errcode.ErrTokenAuthTokenChanged
}
return nil
}
func checkPwdAndStatus(userPwd, inPwd, salt string, stat int16) error {
// 账号是否禁用
if stat == vars.DisabledStatus {
printer.Error("检查账号状态异常: 账号已禁用")
return errcode.ErrUnavailable
}
// 统一使用 bcrypt 比对,不再保留 MD5 兼容分支
if err := bcrypt.CompareHashAndPassword([]byte(userPwd), []byte(inPwd+salt)); err != nil {
printer.Error("密码验证失败: bcrypt 比对不通过")
return errcode.ErrPassword
}
return nil
}
func checkAppKeyAndStatus(userPwd, pwd, salt string, stat int16) error {
// AppKey 与账号密码同源存储bcrypt(密码+salt)),必须做哈希比对,禁止明文相等比对密码列
if err := bcrypt.CompareHashAndPassword([]byte(userPwd), []byte(pwd+salt)); err != nil {
printer.Error("检查密码异常: 密码不匹配")
return errcode.ErrPassword
}
if stat == vars.DisabledStatus {
printer.Error("检查账号状态异常: 账号已禁用")
return errcode.ErrUnavailable
}
return nil
}