feat: Implement Oauth2 login log service and repository

- Added Oauth2LogLoginService for managing user authorization logs.
- Implemented methods for inserting logs, cleaning logs, and exporting log data.
- Created a new file for Oauth2 login log service.

refactor: Remove unused open_api module

- Deleted the open_api.go file as it was not utilized in the project.

fix: Update error codes in SysProfileController

- Changed error codes for binding errors and user authentication errors to more descriptive values.

fix: Update cache handling in SysConfig and SysDictType services

- Modified Redis set operations to include expiration time for cached values.

refactor: Update middleware authorization checks

- Replaced PreAuthorize middleware with AuthorizeUser across multiple routes in system and tool modules for consistency.

chore: Clean up trace and ws modules

- Updated middleware authorization in trace and ws modules to use AuthorizeUser.
This commit is contained in:
TsMask
2025-04-27 11:07:34 +08:00
parent b29a36e7b5
commit 56991a0b49
72 changed files with 2334 additions and 873 deletions

View File

@@ -19,9 +19,9 @@ func Setup(router *gin.Engine) {
guideGroup := router.Group("/bootloader")
{
guideGroup.POST("", controller.NewBootloader.Start)
guideGroup.PUT("", middleware.PreAuthorize(nil), controller.NewBootloader.Done)
guideGroup.DELETE("", middleware.PreAuthorize(nil), controller.NewBootloader.Reset)
guideGroup.PUT("/account", middleware.PreAuthorize(nil), controller.NewBootloader.Account)
guideGroup.PUT("", middleware.AuthorizeUser(nil), controller.NewBootloader.Done)
guideGroup.DELETE("", middleware.AuthorizeUser(nil), controller.NewBootloader.Reset)
guideGroup.PUT("/account", middleware.AuthorizeUser(nil), controller.NewBootloader.Account)
}
// 验证码操作
@@ -34,20 +34,17 @@ func Setup(router *gin.Engine) {
controller.NewCaptcha.Image,
)
// 账号身份操作处理
// 账号身份操作
{
router.POST("/login",
router.POST("/auth/login",
middleware.RateLimit(middleware.LimitOption{
Time: 180,
Count: 15,
Type: middleware.LIMIT_IP,
}),
middleware.CryptoApi(true, true),
controller.NewAccount.Login,
)
router.GET("/me", middleware.PreAuthorize(nil), controller.NewAccount.Me)
router.GET("/router", middleware.PreAuthorize(nil), controller.NewAccount.Router)
router.POST("/logout",
router.POST("/auth/logout",
middleware.RateLimit(middleware.LimitOption{
Time: 120,
Count: 15,
@@ -55,17 +52,32 @@ func Setup(router *gin.Engine) {
}),
controller.NewAccount.Logout,
)
router.POST("/auth/refresh-token",
middleware.RateLimit(middleware.LimitOption{
Time: 60,
Count: 5,
Type: middleware.LIMIT_IP,
}),
controller.NewAccount.RefreshToken,
)
router.GET("/me",
middleware.AuthorizeUser(nil),
controller.NewAccount.Me,
)
router.GET("/router",
middleware.AuthorizeUser(nil),
controller.NewAccount.Router,
)
}
// 账号注册操作
{
router.POST("/register",
router.POST("/auth/register",
middleware.RateLimit(middleware.LimitOption{
Time: 300,
Count: 10,
Type: middleware.LIMIT_IP,
}),
middleware.CryptoApi(true, true),
controller.NewRegister.Register,
)
}

View File

@@ -2,7 +2,7 @@ package controller
import (
"fmt"
"strings"
"time"
"be.ems/src/framework/config"
"be.ems/src/framework/constants"
@@ -10,6 +10,7 @@ import (
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/token"
"be.ems/src/framework/utils/parse"
"be.ems/src/modules/auth/model"
"be.ems/src/modules/auth/service"
systemModelVO "be.ems/src/modules/system/model/vo"
@@ -34,7 +35,7 @@ type AccountController struct {
// Login 系统登录
//
// POST /login
// POST /auth/login
//
// @Tags common/authorization
// @Accept json
@@ -43,13 +44,13 @@ type AccountController struct {
// @Success 200 {object} object "Response Results"
// @Summary System Login
// @Description System Login
// @Router /login [post]
// @Router /auth/login [post]
func (s AccountController) Login(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body model.LoginBody
if err := c.ShouldBindJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(40422, errMsgs))
c.JSON(422, resp.CodeMsg(422001, errMsgs))
return
}
@@ -59,27 +60,31 @@ func (s AccountController) Login(c *gin.Context) {
// 校验验证码 根据错误信息,创建系统访问记录
if err := s.accountService.ValidateCaptcha(body.Code, body.UUID); err != nil {
msg := fmt.Sprintf("%s code: %s", err.Error(), body.Code)
msg := fmt.Sprintf("%s code %s", err.Error(), body.Code)
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_NO, msg,
[4]string{ipaddr, location, os, browser},
)
c.JSON(400, resp.CodeMsg(40012, i18n.TKey(language, err.Error())))
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
// 登录用户信息
loginUser, err := s.accountService.ByUsername(body.Username, body.Password)
info, err := s.accountService.ByUsername(body.Username, body.Password)
if err != nil {
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_NO, err.Error(),
[4]string{ipaddr, location, os, browser},
)
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
}
data := map[string]any{}
if !config.IsSystemUser(loginUser.UserId) {
if !config.IsSystemUser(info.UserId) {
// 强制改密码
forcePasswdChange, err := s.accountService.PasswordCountOrExpireTime(loginUser.User.LoginCount, loginUser.User.PasswordUpdateTime)
forcePasswdChange, err := s.accountService.PasswordCountOrExpireTime(info.User.LoginCount, info.User.PasswordUpdateTime)
if err != nil {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, err.Error())))
return
@@ -89,25 +94,132 @@ func (s AccountController) Login(c *gin.Context) {
}
}
// 生成令牌,创建系统访问记录
tokenStr := token.Create(&loginUser, [4]string{ipaddr, location, os, browser})
if tokenStr == "" {
c.JSON(200, resp.Err(nil))
deviceFingerprint := reqctx.DeviceFingerprint(c, info.UserId)
// 生成访问令牌
accessToken, expiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "access")
if accessToken == "" || expiresIn == 0 {
c.JSON(200, resp.ErrMsg("token generation failed"))
return
}
// 生成刷新令牌
refreshToken, refreshExpiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "refresh")
// 记录令牌,创建系统访问记录
token.UserInfoCreate(&info, deviceFingerprint, [4]string{ipaddr, location, os, browser})
s.accountService.UpdateLoginDateAndIP(info)
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_YES, "app.common.loginSuccess",
[4]string{ipaddr, location, os, browser},
)
data["tokenType"] = constants.HEADER_PREFIX
data["accessToken"] = accessToken
data["expiresIn"] = expiresIn
data["refreshToken"] = refreshToken
data["refreshExpiresIn"] = refreshExpiresIn
data["userId"] = info.UserId
c.JSON(200, resp.OkData(data))
}
// Logout 系统登出
//
// POST /auth/logout
func (s AccountController) Logout(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
tokenStr := reqctx.Authorization(c)
if tokenStr != "" {
// 存在token时记录退出信息
userName, err := token.UserInfoRemove(tokenStr)
if err != nil {
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
// 创建系统访问记录
s.sysLogLoginService.Insert(
userName, constants.STATUS_YES, "app.common.logoutSuccess",
[4]string{ipaddr, location, os, browser},
)
}
}
c.JSON(200, resp.OkMsg(i18n.TKey(language, "app.common.logoutSuccess")))
}
// RefreshToken 刷新Token
//
// POST /auth/refresh-token
func (s AccountController) RefreshToken(c *gin.Context) {
var body struct {
RefreshToken string `json:"refreshToken" binding:"required"` // 刷新令牌
}
if err := c.ShouldBindJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(422001, errMsgs))
return
} else {
s.accountService.UpdateLoginDateAndIP(loginUser)
// 登录成功
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_YES, "app.common.loginSuccess",
[4]string{ipaddr, location, os, browser},
)
}
data["accessToken"] = tokenStr
data["tokenType"] = strings.TrimRight(constants.HEADER_PREFIX, " ")
data["expiresIn"] = (loginUser.ExpireTime - loginUser.LoginTime) / 1000
data["userId"] = loginUser.UserId
c.JSON(200, resp.OkData(data))
// 验证刷新令牌是否有效
claims, err := token.UserTokenVerify(body.RefreshToken, "refresh")
if err != nil {
c.JSON(401, resp.CodeMsg(401001, err.Error()))
return
}
userId := parse.Number(claims[constants.JWT_USER_ID])
// 登录用户信息
info, err := s.accountService.ByUserId(userId)
if err != nil {
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
// 设备指纹信息是否一致
deviceId := fmt.Sprint(claims[constants.JWT_DEVICE_ID])
deviceFingerprint := reqctx.DeviceFingerprint(c, userId)
if deviceId != deviceFingerprint {
c.JSON(200, resp.ErrMsg("device fingerprint mismatch"))
return
}
// 生成访问令牌
accessToken, expiresIn := token.UserTokenCreate(userId, deviceFingerprint, "access")
if accessToken == "" || expiresIn == 0 {
c.JSON(200, resp.ErrMsg("token generation failed"))
return
}
// 生成刷新令牌
now := time.Now()
exp, _ := claims.GetExpirationTime()
iat, _ := claims.GetIssuedAt()
refreshExpiresIn := int64(exp.Sub(now).Seconds())
refreshToken := body.RefreshToken
// 如果当前时间大于过期时间的一半,则生成新令牌
halfExp := exp.Add(-(exp.Sub(iat.Time)) / 2)
if now.After(halfExp) {
refreshToken, refreshExpiresIn = token.UserTokenCreate(userId, deviceFingerprint, "refresh")
}
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
// 记录令牌,创建系统访问记录
token.UserInfoCreate(&info, deviceFingerprint, [4]string{ipaddr, location, os, browser})
s.accountService.UpdateLoginDateAndIP(info)
s.sysLogLoginService.Insert(
info.User.UserName, constants.STATUS_YES, "Refresh Access Token Successful",
[4]string{ipaddr, location, os, browser},
)
// 返回访问令牌和刷新令牌
c.JSON(200, resp.OkData(map[string]any{
"tokenType": constants.HEADER_PREFIX,
"accessToken": accessToken,
"expiresIn": expiresIn,
"refreshToken": refreshToken,
"refreshExpiresIn": refreshExpiresIn,
"userId": userId,
}))
}
// Me 登录用户信息
@@ -126,7 +238,7 @@ func (s AccountController) Me(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
info, err := reqctx.LoginUser(c)
if err != nil {
c.JSON(401, resp.CodeMsg(40003, err.Error()))
c.JSON(401, resp.CodeMsg(401002, err.Error()))
return
}
@@ -146,7 +258,6 @@ func (s AccountController) Me(c *gin.Context) {
"roles": roles,
"permissions": perms,
}
if !isSystemUser {
// 强制改密码
forcePasswdChange, _ := s.accountService.PasswordCountOrExpireTime(info.User.LoginCount, info.User.PasswordUpdateTime)
@@ -170,12 +281,11 @@ func (s AccountController) Me(c *gin.Context) {
// @Description Login User Routing Information
// @Router /router [get]
func (s AccountController) Router(c *gin.Context) {
userId := reqctx.LoginUserToUserID(c)
loginUserId := reqctx.LoginUserToUserID(c)
// 前端路由,系统管理员拥有所有
isSystemUser := config.IsSystemUser(userId)
buildMenus := s.accountService.RouteMenus(userId, isSystemUser)
isSystemUser := config.IsSystemUser(loginUserId)
buildMenus := s.accountService.RouteMenus(loginUserId, isSystemUser)
// 闭包函数处理多语言
language := reqctx.AcceptLanguage(c)
var converI18n func(language string, arr *[]systemModelVO.Router)
@@ -191,36 +301,3 @@ func (s AccountController) Router(c *gin.Context) {
c.JSON(200, resp.OkData(buildMenus))
}
// Logout 系统登出
//
// POST /logout
//
// @Tags common/authorization
// @Accept json
// @Produce json
// @Success 200 {object} object "Response Results"
// @Security TokenAuth
// @Summary System Logout
// @Description System Logout
// @Router /logout [post]
func (s AccountController) Logout(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
tokenStr := reqctx.Authorization(c)
if tokenStr != "" {
// 存在token时记录退出信息
userName := token.Remove(tokenStr)
if userName != "" {
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
// 创建系统访问记录
s.sysLogLoginService.Insert(
userName, constants.STATUS_YES, "app.common.logoutSuccess",
[4]string{ipaddr, location, os, browser},
)
}
}
c.JSON(200, resp.OkMsg(i18n.TKey(language, "app.common.logoutSuccess")))
}

View File

@@ -1,8 +1,6 @@
package controller
import (
"strings"
"be.ems/src/framework/constants"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
@@ -52,7 +50,7 @@ func (s *BootloaderController) Start(c *gin.Context) {
}
// 登录用户信息
loginUser := token.TokenInfo{
info := token.UserInfo{
UserId: sysUser.UserId,
DeptId: sysUser.DeptId,
User: sysUser,
@@ -60,23 +58,24 @@ func (s *BootloaderController) Start(c *gin.Context) {
}
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
deviceFingerprint := reqctx.DeviceFingerprint(c, info.UserId)
// 生成令牌,创建系统访问记录
tokenStr := token.Create(&loginUser, [4]string{ipaddr, location, os, browser})
if tokenStr == "" {
c.JSON(200, resp.Err(nil))
// 生成访问令牌
accessToken, expiresIn := token.UserTokenCreate(info.UserId, deviceFingerprint, "access")
if accessToken == "" || expiresIn == 0 {
c.JSON(200, resp.ErrMsg("token generation failed"))
return
} else {
s.accountService.UpdateLoginDateAndIP(loginUser)
}
// 创建系统访问记录
s.accountService.UpdateLoginDateAndIP(info)
c.JSON(200, resp.OkData(map[string]any{
"accessToken": tokenStr,
"tokenType": strings.TrimRight(constants.HEADER_PREFIX, " "),
"expiresIn": (loginUser.ExpireTime - loginUser.LoginTime) / 1000,
"userId": loginUser.UserId,
"tokenType": constants.HEADER_PREFIX,
"accessToken": accessToken,
"expiresIn": expiresIn,
"refreshToken": "",
"refreshExpiresIn": 0,
"userId": info.UserId,
}))
}
@@ -102,7 +101,7 @@ func (s *BootloaderController) Done(c *gin.Context) {
}
// 清除授权信息
token.Remove(reqctx.Authorization(c))
token.UserInfoRemove(reqctx.Authorization(c))
c.JSON(200, resp.Ok(nil))
}
@@ -127,7 +126,7 @@ func (s *BootloaderController) Reset(c *gin.Context) {
}
// 清除授权信息
token.Remove(reqctx.Authorization(c))
token.UserInfoRemove(reqctx.Authorization(c))
c.JSON(200, resp.Ok(nil))
}

View File

@@ -87,7 +87,7 @@ func (s *CaptchaController) Image(c *gin.Context) {
data["img"] = item.EncodeB64string()
expiration := constants.CAPTCHA_EXPIRATION * time.Second
verifyKey = constants.CACHE_CAPTCHA_CODE + ":" + id
redis.SetByExpire("", verifyKey, answer, expiration)
redis.Set("", verifyKey, answer, expiration)
}
}
if captchaType == constants.CAPTCHA_TYPE_CHAR {
@@ -121,7 +121,7 @@ func (s *CaptchaController) Image(c *gin.Context) {
data["img"] = item.EncodeB64string()
expiration := constants.CAPTCHA_EXPIRATION * time.Second
verifyKey = constants.CACHE_CAPTCHA_CODE + ":" + id
redis.SetByExpire("", verifyKey, answer, expiration)
redis.Set("", verifyKey, answer, expiration)
}
}

View File

@@ -29,15 +29,32 @@ type RegisterController struct {
sysLogLoginService *systemService.SysLogLogin // 系统登录访问服务
}
// 账号注册
// Register 账号注册
//
// GET /register
func (s *RegisterController) Register(c *gin.Context) {
// POST /auth/register
func (s RegisterController) Register(c *gin.Context) {
language := reqctx.AcceptLanguage(c)
var body model.RegisterBody
if err := c.ShouldBindJSON(&body); err != nil {
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
c.JSON(422, resp.CodeMsg(40422, errMsgs))
c.JSON(422, resp.CodeMsg(422001, errMsgs))
return
}
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
// 校验验证码
err := s.registerService.ValidateCaptcha(body.Code, body.UUID)
// 根据错误信息,创建系统访问记录
if err != nil {
msg := fmt.Sprintf("%s code %s", err.Error(), body.Code)
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_NO, msg,
[4]string{ipaddr, location, os, browser},
)
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
@@ -47,15 +64,9 @@ func (s *RegisterController) Register(c *gin.Context) {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "register.errUsername")))
return
}
// if !regular.ValidPassword(body.Password) {
// // 登录密码至少包含大小写字母、数字、特殊符号且不少于6位
// c.JSON(200, resp.ErrMsg(i18n.TKey(language, "register.errPasswd")))
// return
// }
// 检查用户密码策略强度
ok, errMsg := s.registerService.ValidatePasswordPolicy(body.Password, language)
if !ok {
c.JSON(200, resp.ErrMsg(errMsg))
if !regular.ValidPassword(body.Password) {
// 登录密码至少包含大小写字母、数字、特殊符号且不少于6位
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "register.errPasswd")))
return
}
if body.Password != body.ConfirmPassword {
@@ -64,26 +75,7 @@ func (s *RegisterController) Register(c *gin.Context) {
return
}
// 当前请求信息
ipaddr, location := reqctx.IPAddrLocation(c)
os, browser := reqctx.UaOsBrowser(c)
// 校验验证码
err := s.registerService.ValidateCaptcha(
body.Code,
body.UUID,
)
// 根据错误信息,创建系统访问记录
if err != nil {
msg := err.Error() + " code: " + body.Code
s.sysLogLoginService.Insert(
body.Username, constants.STATUS_NO, msg,
[4]string{ipaddr, location, os, browser},
)
c.JSON(200, resp.ErrMsg(err.Error()))
return
}
// 进行注册
userId, err := s.registerService.ByUserName(body.Username, body.Password)
if err == nil {
msg := i18n.TTemplate(language, "register.successMsg", map[string]any{"name": body.Username, "id": userId})

View File

@@ -56,25 +56,25 @@ func (s *Account) ValidateCaptcha(code, uuid string) error {
}
// ByUsername 登录创建用户信息
func (s Account) ByUsername(username, password string) (token.TokenInfo, error) {
tokenInfo := token.TokenInfo{}
func (s Account) ByUsername(username, password string) (token.UserInfo, error) {
info := token.UserInfo{}
// 检查密码重试次数
retryKey, retryCount, lockTime, err := s.passwordRetryCount(username)
if err != nil {
return tokenInfo, err
return info, err
}
// 查询用户登录账号
sysUser := s.sysUserService.FindByUserName(username)
if sysUser.UserName != username {
return tokenInfo, fmt.Errorf("login.errNameOrPasswd")
return info, fmt.Errorf("login.errNameOrPasswd")
}
if sysUser.DelFlag == constants.STATUS_YES {
return tokenInfo, fmt.Errorf("login.errDelFlag")
return info, fmt.Errorf("login.errDelFlag")
}
if sysUser.StatusFlag == constants.STATUS_NO {
return tokenInfo, fmt.Errorf("login.errStatus")
return info, fmt.Errorf("login.errStatus")
}
// 检验用户密码
@@ -82,31 +82,61 @@ func (s Account) ByUsername(username, password string) (token.TokenInfo, error)
if compareBool {
s.CleanLoginRecordCache(sysUser.UserName) // 清除错误记录次数
} else {
_ = redis.SetByExpire("", retryKey, retryCount+1, lockTime)
return tokenInfo, fmt.Errorf("login.errNameOrPasswd")
_ = redis.Set("", retryKey, retryCount+1, lockTime)
return info, fmt.Errorf("login.errNameOrPasswd")
}
// 登录用户信息
tokenInfo.UserId = sysUser.UserId
tokenInfo.DeptId = sysUser.DeptId
tokenInfo.User = sysUser
info.UserId = sysUser.UserId
info.DeptId = sysUser.DeptId
info.User = sysUser
// 用户权限组标识
if config.IsSystemUser(sysUser.UserId) {
tokenInfo.Permissions = []string{constants.SYS_PERMISSION_SYSTEM}
info.Permissions = []string{constants.SYS_PERMISSION_SYSTEM}
} else {
perms := s.sysMenuService.FindPermsByUserId(sysUser.UserId)
tokenInfo.Permissions = parse.RemoveDuplicates(perms)
info.Permissions = parse.RemoveDuplicates(perms)
}
return tokenInfo, nil
return info, nil
}
// ByUserId 用户ID刷新令牌创建用户信息
func (s Account) ByUserId(userId int64) (token.UserInfo, error) {
info := token.UserInfo{}
// 查询用户登录账号
sysUser := s.sysUserService.FindById(userId)
if sysUser.UserId != userId {
return info, fmt.Errorf("user does not exist")
}
if sysUser.DelFlag == constants.STATUS_YES {
return info, fmt.Errorf("sorry, your account has been deleted. Sorry, your account has been deleted")
}
if sysUser.StatusFlag == constants.STATUS_NO {
return info, fmt.Errorf("sorry, your account has been disabled")
}
// 登录用户信息
info.UserId = sysUser.UserId
info.DeptId = sysUser.DeptId
info.User = sysUser
// 用户权限组标识
if config.IsSystemUser(sysUser.UserId) {
info.Permissions = []string{constants.SYS_PERMISSION_SYSTEM}
} else {
perms := s.sysMenuService.FindPermsByUserId(sysUser.UserId)
info.Permissions = parse.RemoveDuplicates(perms)
}
return info, nil
}
// UpdateLoginDateAndIP 更新登录时间和IP
func (s Account) UpdateLoginDateAndIP(tokenInfo token.TokenInfo) bool {
user := s.sysUserService.FindById(tokenInfo.UserId)
func (s Account) UpdateLoginDateAndIP(info token.UserInfo) bool {
user := s.sysUserService.FindById(info.UserId)
user.Password = "" // 密码不更新
user.LoginCount += 1
user.LoginIp = tokenInfo.LoginIp
user.LoginTime = tokenInfo.LoginTime
user.LoginIp = info.LoginIp
user.LoginTime = info.LoginTime
return s.sysUserService.Update(user) > 0
}

View File

@@ -1,13 +1,10 @@
package service
import (
"encoding/json"
"fmt"
"regexp"
"be.ems/src/framework/constants"
"be.ems/src/framework/database/redis"
"be.ems/src/framework/i18n"
"be.ems/src/framework/utils/parse"
systemModel "be.ems/src/modules/system/model"
systemService "be.ems/src/modules/system/service"
@@ -81,7 +78,7 @@ func (s Register) ByUserName(username, password string) (int64, error) {
if insertId > 0 {
return insertId, nil
}
return 0, fmt.Errorf("failed to register user [%s]. Please contact the system administrator", username)
return 0, fmt.Errorf("failed to register user [%s]. Please contact the GM", username)
}
// registerRoleInit 注册初始角色
@@ -93,45 +90,3 @@ func (s Register) registerRoleInit() []int64 {
func (s Register) registerPostInit() []int64 {
return []int64{}
}
// ValidatePasswordPolicy 判断密码策略强度
func (s Register) ValidatePasswordPolicy(password string, errLang string) (bool, string) {
passwordPolicyStr := s.sysConfigService.FindValueByKey("sys.user.passwordPolicy")
if passwordPolicyStr == "" {
// 未配置密码策略
return false, i18n.TKey(errLang, "config.sys.user.passwordPolicyNot")
}
var policy struct {
MinLength int `json:"minLength"`
SpecialChars int `json:"specialChars"`
Uppercase int `json:"uppercase"`
Lowercase int `json:"lowercase"`
}
err := json.Unmarshal([]byte(passwordPolicyStr), &policy)
if err != nil {
return false, err.Error()
}
errMsg := i18n.TTemplate(errLang, "sys.user.passwordPolicyError", map[string]any{
"minLength": policy.MinLength,
"specialChars": policy.SpecialChars,
"uppercase": policy.Uppercase,
"lowercase": policy.Lowercase,
})
specialChars := len(regexp.MustCompile(`[!@#$%^&*(),.?":{}|<>]`).FindAllString(password, -1))
if specialChars < policy.SpecialChars {
return false, errMsg
}
uppercase := len(regexp.MustCompile(`[A-Z]`).FindAllString(password, -1))
if uppercase < policy.Uppercase {
return false, errMsg
}
lowercase := len(regexp.MustCompile(`[a-z]`).FindAllString(password, -1))
if lowercase < policy.Lowercase {
return false, errMsg
}
return true, ""
}