Files
schisandra-album-cloud-micr…/app/auth/api/internal/logic/user/account_login_logic.go
2024-12-24 18:01:54 +08:00

190 lines
5.6 KiB
Go

package user
import (
"context"
errors2 "errors"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/mssola/useragent"
"net/http"
model2 "schisandra-album-cloud-microservices/app/auth/model/mysql/model"
query2 "schisandra-album-cloud-microservices/app/auth/model/mysql/query"
"schisandra-album-cloud-microservices/common/captcha/verify"
"schisandra-album-cloud-microservices/common/constant"
"schisandra-album-cloud-microservices/common/errors"
"schisandra-album-cloud-microservices/common/i18n"
"schisandra-album-cloud-microservices/common/jwt"
"schisandra-album-cloud-microservices/common/utils"
"time"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
)
type AccountLoginLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAccountLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AccountLoginLogic {
return &AccountLoginLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AccountLoginLogic) AccountLogin(r *http.Request, req *types.AccountLoginRequest) (resp *types.LoginResponse, err error) {
verifyResult := verify.VerifyRotateCaptcha(l.ctx, l.svcCtx.RedisClient, req.Angle, req.Key)
if !verifyResult {
return nil, errors.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "captcha.verificationFailure"))
}
user := l.svcCtx.DB.ScaAuthUser
var selectedUser query2.IScaAuthUserDo
switch {
case utils.IsPhone(req.Account):
selectedUser = user.Where(user.Phone.Eq(req.Account))
case utils.IsEmail(req.Account):
selectedUser = user.Where(user.Email.Eq(req.Account))
case utils.IsUsername(req.Account):
selectedUser = user.Where(user.Username.Eq(req.Account))
default:
return nil, errors.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "login.invalidAccount"))
}
userInfo, err := selectedUser.First()
if err != nil {
if errors2.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "login.notFoundAccount"))
}
return nil, err
}
if !utils.Verify(userInfo.Password, req.Password) {
return nil, errors.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "login.invalidPassword"))
}
data, err := HandleLoginJWT(userInfo, l.svcCtx, req.AutoLogin, r, l.ctx)
if err != nil {
return nil, err
}
return data, nil
}
// HandleLoginJWT 处理用户登录
func HandleLoginJWT(user *model2.ScaAuthUser, svcCtx *svc.ServiceContext, autoLogin bool, r *http.Request, ctx context.Context) (*types.LoginResponse, error) {
// 获取用户登录设备
err := GetUserLoginDevice(user.UID, r, svcCtx.Ip2Region, svcCtx.DB)
if err != nil {
return nil, err
}
// 生成jwt token
accessToken, expireAt := jwt.GenerateAccessToken(svcCtx.Config.Auth.AccessSecret, jwt.AccessJWTPayload{
UserID: user.UID,
Type: constant.JWT_TYPE_ACCESS,
})
var days time.Duration
if autoLogin {
days = 3 * 24 * time.Hour
} else {
days = time.Hour * 24
}
refreshToken := jwt.GenerateRefreshToken(svcCtx.Config.Auth.AccessSecret, jwt.RefreshJWTPayload{
UserID: user.UID,
Type: constant.JWT_TYPE_REFRESH,
}, days)
data := types.LoginResponse{
AccessToken: accessToken,
ExpireAt: expireAt,
UID: user.UID,
Username: user.Username,
Nickname: user.Nickname,
Avatar: user.Avatar,
Status: user.Status,
}
redisToken := types.RedisToken{
AccessToken: accessToken,
RefreshToken: refreshToken,
UID: user.UID,
Revoked: false,
GeneratedAt: time.Now().Format(constant.TimeFormat),
AllowAgent: r.UserAgent(),
GeneratedIP: utils.GetClientIP(r),
UpdatedAt: time.Now().Format(constant.TimeFormat),
}
err = svcCtx.RedisClient.Set(ctx, constant.UserTokenPrefix+user.UID, redisToken, days).Err()
if err != nil {
return nil, err
}
return &data, nil
}
// GetUserLoginDevice 获取用户登录设备
func GetUserLoginDevice(userId string, r *http.Request, ip2location *xdb.Searcher, DB *query2.Query) error {
userAgent := r.UserAgent()
if userAgent == "" {
return errors2.New("user agent not found")
}
ip := utils.GetClientIP(r)
location, err := ip2location.SearchByStr(ip)
if err != nil {
return err
}
location = utils.RemoveZeroAndAdjust(location)
ua := useragent.New(userAgent)
isBot := ua.Bot()
browser, browserVersion := ua.Browser()
os := ua.OS()
mobile := ua.Mobile()
mozilla := ua.Mozilla()
platform := ua.Platform()
engine, engineVersion := ua.Engine()
var newIsBot int64 = 0
var newIsMobile int64 = 0
if isBot {
newIsBot = 1
}
if mobile {
newIsMobile = 1
}
userDevice := DB.ScaAuthUserDevice
device, err := userDevice.Where(userDevice.UserID.Eq(userId), userDevice.IP.Eq(ip), userDevice.Agent.Eq(userAgent)).First()
if err != nil && !errors2.Is(err, gorm.ErrRecordNotFound) {
return err
}
newDevice := &model2.ScaAuthUserDevice{
UserID: userId,
Bot: newIsBot,
Agent: userAgent,
Browser: browser,
BrowserVersion: browserVersion,
EngineName: engine,
EngineVersion: engineVersion,
IP: ip,
Location: location,
OperatingSystem: os,
Mobile: newIsMobile,
Mozilla: mozilla,
Platform: platform,
}
if device == nil {
// 创建新的设备记录
err = DB.ScaAuthUserDevice.Create(newDevice)
if err != nil {
return err
}
return nil
} else {
resultInfo, err := userDevice.Where(userDevice.ID.Eq(device.ID)).Updates(newDevice)
if err != nil || resultInfo.RowsAffected == 0 {
return errors2.New("update device failed")
}
return nil
}
}