♻️ refactored login-related code

This commit is contained in:
2024-12-21 00:51:59 +08:00
parent 40d073db0f
commit f213644aa9
33 changed files with 802 additions and 535 deletions

View File

@@ -3,7 +3,8 @@ package user
import (
"context"
"errors"
"github.com/rbcervilla/redisstore/v9"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/mssola/useragent"
"net/http"
"time"
@@ -65,19 +66,20 @@ func (l *AccountLoginLogic) AccountLogin(w http.ResponseWriter, r *http.Request,
if !utils.Verify(userInfo.Password, req.Password) {
return response.ErrorWithI18n(l.ctx, "login.invalidPassword"), nil
}
data, err := HandleUserLogin(userInfo, l.svcCtx, req.AutoLogin, r, w, l.ctx)
data, err := HandleLoginJWT(userInfo, l.svcCtx, req.AutoLogin, r, l.ctx)
if err != nil {
return nil, err
}
// 记录用户登录设备
if err = GetUserLoginDevice(userInfo.UID, r, l.svcCtx.Ip2Region, l.svcCtx.DB); err != nil {
return nil, err
}
return response.SuccessWithData(data), nil
}
// HandleUserLogin 处理用户登录
func HandleUserLogin(user *model.ScaAuthUser, svcCtx *svc.ServiceContext, autoLogin bool, r *http.Request, w http.ResponseWriter, ctx context.Context) (*types.LoginResponse, error) {
// HandleLoginJWT 处理用户登录
func HandleLoginJWT(user *model.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 := jwt.GenerateAccessToken(svcCtx.Config.Auth.AccessSecret, jwt.AccessJWTPayload{
UserID: user.UID,
@@ -85,9 +87,9 @@ func HandleUserLogin(user *model.ScaAuthUser, svcCtx *svc.ServiceContext, autoLo
})
var days time.Duration
if autoLogin {
days = 7 * 24 * time.Hour
days = 24 * time.Hour
} else {
days = time.Hour * 24
days = time.Hour * 1
}
refreshToken := jwt.GenerateRefreshToken(svcCtx.Config.Auth.AccessSecret, jwt.RefreshJWTPayload{
UserID: user.UID,
@@ -106,29 +108,76 @@ func HandleUserLogin(user *model.ScaAuthUser, svcCtx *svc.ServiceContext, autoLo
AccessToken: accessToken,
RefreshToken: refreshToken,
UID: user.UID,
Revoked: false,
}
err := svcCtx.RedisClient.Set(ctx, constant.UserTokenPrefix+user.UID, redisToken, days).Err()
if err != nil {
return nil, err
}
err = HandlerSession(r, w, user.UID, svcCtx.Session)
err = svcCtx.RedisClient.Set(ctx, constant.UserTokenPrefix+user.UID, redisToken, days).Err()
if err != nil {
return nil, err
}
return &data, nil
}
// HandlerSession is a function to set the user_id in the session
func HandlerSession(r *http.Request, w http.ResponseWriter, userID string, redisSession *redisstore.RedisStore) error {
session, err := redisSession.Get(r, constant.SESSION_KEY)
// GetUserLoginDevice 获取用户登录设备
func GetUserLoginDevice(userId string, r *http.Request, ip2location *xdb.Searcher, DB *query.Query) error {
userAgent := r.UserAgent()
if userAgent == "" {
return errors.New("user agent not found")
}
ip := utils.GetClientIP(r)
location, err := ip2location.SearchByStr(ip)
if err != nil {
return err
}
session.Values["user_id"] = userID
err = session.Save(r, w)
if err != nil {
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 && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
return nil
newDevice := &model.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 errors.New("update device failed")
}
return nil
}
}

View File

@@ -1,115 +0,0 @@
package user
import (
"context"
"errors"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/mssola/useragent"
"gorm.io/gorm"
"net/http"
"schisandra-album-cloud-microservices/app/core/api/common/jwt"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/model"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/query"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetUserDeviceLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetUserDeviceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserDeviceLogic {
return &GetUserDeviceLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetUserDeviceLogic) GetUserDevice(r *http.Request, w http.ResponseWriter, req *types.UserDeviceRequest) (resp *types.Response, err error) {
token, ok := jwt.ParseAccessToken(l.svcCtx.Config.Auth.AccessSecret, req.AccessToken)
if !ok {
return response.Error(), nil
}
err = HandlerSession(r, w, token.UserID, l.svcCtx.Session)
if err != nil {
return nil, err
}
err = GetUserLoginDevice(token.UserID, r, l.svcCtx.Ip2Region, l.svcCtx.DB)
if err != nil {
return nil, err
}
return response.Success(), nil
}
// GetUserLoginDevice 获取用户登录设备
func GetUserLoginDevice(userId string, r *http.Request, ip2location *xdb.Searcher, DB *query.Query) error {
userAgent := r.Header.Get("User-Agent")
if userAgent == "" {
return errors.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 && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
newDevice := &model.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 errors.New("update device failed")
}
return nil
}
}

View File

@@ -0,0 +1,63 @@
package user
import (
"context"
"encoding/json"
"github.com/ArtisanCloud/PowerWeChat/v3/src/basicService/qrCode/response"
"net/http"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
response2 "schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"time"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetWechatOffiaccountQrcodeLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetWechatOffiaccountQrcodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWechatOffiaccountQrcodeLogic {
return &GetWechatOffiaccountQrcodeLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetWechatOffiaccountQrcodeLogic) GetWechatOffiaccountQrcode(r *http.Request, req *types.OAuthWechatRequest) (resp *types.Response, err error) {
ip := utils.GetClientIP(r) // 使用工具函数获取客户端IP
key := constant.UserQrcodePrefix + ip
// 从Redis获取二维码数据
qrcode := l.svcCtx.RedisClient.Get(l.ctx, key).Val()
if qrcode != "" {
data := new(response.ResponseQRCodeCreate)
if err = json.Unmarshal([]byte(qrcode), data); err != nil {
return nil, err
}
return response2.SuccessWithData(data.Url), nil
}
// 生成临时二维码
data, err := l.svcCtx.WechatOfficial.QRCode.Temporary(l.ctx, req.Client_id, 7*24*3600)
if err != nil {
return nil, err
}
// 序列化数据并存储到Redis
serializedData, err := json.Marshal(data)
if err != nil {
return nil, err
}
if err = l.svcCtx.RedisClient.Set(l.ctx, key, serializedData, time.Hour*24*7).Err(); err != nil {
return nil, err
}
return response2.SuccessWithData(data.Url), nil
}

View File

@@ -79,32 +79,22 @@ func (l *PhoneLoginLogic) PhoneLogin(r *http.Request, w http.ResponseWriter, req
_ = tx.Rollback()
return nil, err
}
data, err := HandleUserLogin(user, l.svcCtx, req.AutoLogin, r, w, l.ctx)
data, err := HandleLoginJWT(user, l.svcCtx, req.AutoLogin, r, l.ctx)
if err != nil {
_ = tx.Rollback()
return nil, err
}
// 记录用户登录设备
if err = GetUserLoginDevice(user.UID, r, l.svcCtx.Ip2Region, l.svcCtx.DB); err != nil {
_ = tx.Rollback()
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, err
}
return response.SuccessWithData(data), nil
} else {
data, err := HandleUserLogin(userInfo, l.svcCtx, req.AutoLogin, r, w, l.ctx)
data, err := HandleLoginJWT(userInfo, l.svcCtx, req.AutoLogin, r, l.ctx)
if err != nil {
_ = tx.Rollback()
return nil, err
}
// 记录用户登录设备
if err = GetUserLoginDevice(userInfo.UID, r, l.svcCtx.Ip2Region, l.svcCtx.DB); err != nil {
_ = tx.Rollback()
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, err

View File

@@ -0,0 +1,107 @@
package user
import (
"context"
"errors"
"github.com/yitter/idgenerator-go/idgen"
"gorm.io/gorm"
"net/http"
"schisandra-album-cloud-microservices/app/core/api/common/constant"
randomname "schisandra-album-cloud-microservices/app/core/api/common/random_name"
"schisandra-album-cloud-microservices/app/core/api/common/response"
"schisandra-album-cloud-microservices/app/core/api/common/utils"
"schisandra-album-cloud-microservices/app/core/api/repository/mysql/model"
"strconv"
"schisandra-album-cloud-microservices/app/core/api/internal/svc"
"schisandra-album-cloud-microservices/app/core/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type WechatOffiaccountLoginLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewWechatOffiaccountLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *WechatOffiaccountLoginLogic {
return &WechatOffiaccountLoginLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *WechatOffiaccountLoginLogic) WechatOffiaccountLogin(r *http.Request, req *types.WechatOffiaccountLoginRequest) (resp *types.Response, err error) {
tx := l.svcCtx.DB.Begin()
userSocial := l.svcCtx.DB.ScaAuthUserSocial
socialUser, err := tx.ScaAuthUserSocial.Where(userSocial.OpenID.Eq(req.Openid), userSocial.Source.Eq(constant.OAuthSourceWechat)).First()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
if socialUser == nil {
// 创建用户
uid := idgen.NextId()
uidStr := strconv.FormatInt(uid, 10)
avatar := utils.GenerateAvatar(uidStr)
name := randomname.GenerateName()
addUser := &model.ScaAuthUser{
UID: uidStr,
Avatar: avatar,
Username: req.Openid,
Nickname: name,
Gender: constant.Male,
}
err = tx.ScaAuthUser.Create(addUser)
if err != nil {
_ = tx.Rollback()
return nil, err
}
newSocialUser := &model.ScaAuthUserSocial{
UserID: uidStr,
OpenID: req.Openid,
Source: constant.OAuthSourceWechat,
}
err = tx.ScaAuthUserSocial.Create(newSocialUser)
if err != nil {
_ = tx.Rollback()
return nil, err
}
if res, err := l.svcCtx.CasbinEnforcer.AddRoleForUser(uidStr, constant.User); !res || err != nil {
_ = tx.Rollback()
return nil, err
}
data, err := HandleLoginJWT(addUser, l.svcCtx, true, r, l.ctx)
if err != nil {
_ = tx.Rollback()
return nil, err
}
if err = tx.Commit(); err != nil {
return nil, err
}
return response.SuccessWithData(data), nil
} else {
authUser := l.svcCtx.DB.ScaAuthUser
authUserInfo, err := tx.ScaAuthUser.Where(authUser.UID.Eq(socialUser.UserID)).First()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
_ = tx.Rollback()
return nil, err
}
data, err := HandleLoginJWT(authUserInfo, l.svcCtx, true, r, l.ctx)
if err != nil {
_ = tx.Rollback()
return nil, err
}
if err = tx.Commit(); err != nil {
return nil, err
}
return response.SuccessWithData(data), nil
}
}