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:
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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")))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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, ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user