feat: 新增第三方登录认证和管理
This commit is contained in:
@@ -23,38 +23,84 @@ func Setup(router *gin.Engine) {
|
||||
)
|
||||
|
||||
// 账号身份操作
|
||||
account := controller.NewAccount
|
||||
accountGroup := router.Group("/auth")
|
||||
{
|
||||
router.POST("/auth/login",
|
||||
accountGroup.POST("/login",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 180,
|
||||
Count: 15,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
controller.NewAccount.Login,
|
||||
account.Login,
|
||||
)
|
||||
router.POST("/auth/logout",
|
||||
accountGroup.POST("/logout",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 120,
|
||||
Count: 15,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
controller.NewAccount.Logout,
|
||||
account.Logout,
|
||||
)
|
||||
router.POST("/auth/refresh-token",
|
||||
accountGroup.POST("/refresh-token",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 60,
|
||||
Count: 5,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
controller.NewAccount.RefreshToken,
|
||||
account.RefreshToken,
|
||||
)
|
||||
router.GET("/me",
|
||||
middleware.AuthorizeUser(nil),
|
||||
controller.NewAccount.Me,
|
||||
account.Me,
|
||||
)
|
||||
router.GET("/router",
|
||||
middleware.AuthorizeUser(nil),
|
||||
controller.NewAccount.Router,
|
||||
account.Router,
|
||||
)
|
||||
}
|
||||
|
||||
// 登录认证源
|
||||
{
|
||||
accountGroup.GET("/login/source",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 300,
|
||||
Count: 60,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
account.LoginSource,
|
||||
)
|
||||
accountGroup.POST("/login/ldap",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 180,
|
||||
Count: 15,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
account.LDAP,
|
||||
)
|
||||
accountGroup.POST("/login/smtp",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 180,
|
||||
Count: 15,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
account.SMTP,
|
||||
)
|
||||
accountGroup.GET("/login/oauth2/authorize",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 180,
|
||||
Count: 15,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
account.OAuth2CodeURL,
|
||||
)
|
||||
accountGroup.POST("/login/oauth2/token",
|
||||
middleware.RateLimit(middleware.LimitOption{
|
||||
Time: 180,
|
||||
Count: 15,
|
||||
Type: middleware.LIMIT_IP,
|
||||
}),
|
||||
account.OAuth2Token,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,13 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/i18n"
|
||||
"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"
|
||||
systemService "be.ems/src/modules/system/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -33,95 +30,6 @@ type AccountController struct {
|
||||
sysLogLoginService *systemService.SysLogLogin // 系统登录访问
|
||||
}
|
||||
|
||||
// Login 系统登录
|
||||
//
|
||||
// POST /auth/login
|
||||
//
|
||||
// @Tags common/authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body object true "Request Param"
|
||||
// @Success 200 {object} object "Response Results"
|
||||
// @Summary System Login
|
||||
// @Description System Login
|
||||
// @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(resp.CODE_PARAM_PARSER, errMsgs))
|
||||
return
|
||||
}
|
||||
|
||||
// 当前请求信息
|
||||
ipaddr, location := reqctx.IPAddrLocation(c)
|
||||
os, browser := reqctx.UaOsBrowser(c)
|
||||
|
||||
// 校验验证码 根据错误信息,创建系统访问记录
|
||||
if err := s.accountService.ValidateCaptcha(body.Code, body.UUID); 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(i18n.TKey(language, err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
// 登录用户信息
|
||||
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(info.UserId) {
|
||||
// 强制改密码
|
||||
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
|
||||
}
|
||||
if forcePasswdChange {
|
||||
data["forcePasswdChange"] = true
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -222,82 +130,10 @@ func (s AccountController) RefreshToken(c *gin.Context) {
|
||||
}))
|
||||
}
|
||||
|
||||
// Me 登录用户信息
|
||||
// LoginSource 登录认证源
|
||||
//
|
||||
// GET /me
|
||||
//
|
||||
// @Tags common/authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} object "Response Results"
|
||||
// @Security TokenAuth
|
||||
// @Summary Login User Information
|
||||
// @Description Login User Information
|
||||
// @Router /me [get]
|
||||
func (s AccountController) Me(c *gin.Context) {
|
||||
language := reqctx.AcceptLanguage(c)
|
||||
info, err := reqctx.LoginUser(c)
|
||||
if err != nil {
|
||||
c.JSON(401, resp.CodeMsg(resp.CODE_AUTH_INVALID, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 角色权限集合,系统管理员拥有所有权限
|
||||
isSystemUser := config.IsSystemUser(info.UserId)
|
||||
roles, perms := s.accountService.RoleAndMenuPerms(info.UserId, isSystemUser)
|
||||
|
||||
info.User.NickName = i18n.TKey(language, info.User.NickName)
|
||||
info.User.Remark = i18n.TKey(language, info.User.Remark)
|
||||
info.User.Dept.DeptName = i18n.TKey(language, info.User.Dept.DeptName)
|
||||
for ri := range info.User.Roles {
|
||||
info.User.Roles[ri].RoleName = i18n.TKey(language, info.User.Roles[ri].RoleName)
|
||||
}
|
||||
|
||||
data := map[string]any{
|
||||
"user": info.User,
|
||||
"roles": roles,
|
||||
"permissions": perms,
|
||||
}
|
||||
if !isSystemUser {
|
||||
// 强制改密码
|
||||
forcePasswdChange, _ := s.accountService.PasswordCountOrExpireTime(info.User.LoginCount, info.User.PasswordUpdateTime)
|
||||
if forcePasswdChange {
|
||||
data["forcePasswdChange"] = true
|
||||
}
|
||||
}
|
||||
// GET /auth/login/source
|
||||
func (s AccountController) LoginSource(c *gin.Context) {
|
||||
data := s.accountService.LoginSource()
|
||||
c.JSON(200, resp.OkData(data))
|
||||
}
|
||||
|
||||
// Router 登录用户路由信息
|
||||
//
|
||||
// GET /router
|
||||
//
|
||||
// @Tags common/authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} object "Response Results"
|
||||
// @Security TokenAuth
|
||||
// @Summary Login User Routing Information
|
||||
// @Description Login User Routing Information
|
||||
// @Router /router [get]
|
||||
func (s AccountController) Router(c *gin.Context) {
|
||||
loginUserId := reqctx.LoginUserToUserID(c)
|
||||
|
||||
// 前端路由,系统管理员拥有所有
|
||||
isSystemUser := config.IsSystemUser(loginUserId)
|
||||
buildMenus := s.accountService.RouteMenus(loginUserId, isSystemUser)
|
||||
// 闭包函数处理多语言
|
||||
language := reqctx.AcceptLanguage(c)
|
||||
var converI18n func(language string, arr *[]systemModelVO.Router)
|
||||
converI18n = func(language string, arr *[]systemModelVO.Router) {
|
||||
for i := range *arr {
|
||||
(*arr)[i].Meta.Title = i18n.TKey(language, (*arr)[i].Meta.Title)
|
||||
if len((*arr)[i].Children) > 0 {
|
||||
converI18n(language, &(*arr)[i].Children)
|
||||
}
|
||||
}
|
||||
}
|
||||
converI18n(language, &buildMenus)
|
||||
|
||||
c.JSON(200, resp.OkData(buildMenus))
|
||||
}
|
||||
|
||||
93
src/modules/auth/controller/account_info.go
Normal file
93
src/modules/auth/controller/account_info.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/reqctx"
|
||||
"be.ems/src/framework/resp"
|
||||
systemModelVO "be.ems/src/modules/system/model/vo"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Me 登录用户信息
|
||||
//
|
||||
// GET /me
|
||||
//
|
||||
// @Tags common/authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} object "Response Results"
|
||||
// @Security TokenAuth
|
||||
// @Summary Login User Information
|
||||
// @Description Login User Information
|
||||
// @Router /me [get]
|
||||
func (s AccountController) Me(c *gin.Context) {
|
||||
language := reqctx.AcceptLanguage(c)
|
||||
info, err := reqctx.LoginUser(c)
|
||||
if err != nil {
|
||||
c.JSON(401, resp.CodeMsg(resp.CODE_AUTH_INVALID, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 角色权限集合,系统管理员拥有所有权限
|
||||
isSystemUser := config.IsSystemUser(info.UserId)
|
||||
roles, perms := s.accountService.RoleAndMenuPerms(info.UserId, isSystemUser)
|
||||
|
||||
info.User.NickName = i18n.TKey(language, info.User.NickName)
|
||||
info.User.Remark = i18n.TKey(language, info.User.Remark)
|
||||
if info.User.Dept != nil {
|
||||
info.User.Dept.DeptName = i18n.TKey(language, info.User.Dept.DeptName)
|
||||
}
|
||||
for ri := range info.User.Roles {
|
||||
info.User.Roles[ri].RoleName = i18n.TKey(language, info.User.Roles[ri].RoleName)
|
||||
}
|
||||
|
||||
data := map[string]any{
|
||||
"user": info.User,
|
||||
"roles": roles,
|
||||
"permissions": perms,
|
||||
}
|
||||
if !isSystemUser {
|
||||
// 强制改密码
|
||||
forcePasswdChange, _ := s.accountService.PasswordCountOrExpireTime(info.User.LoginCount, info.User.PasswordUpdateTime)
|
||||
if forcePasswdChange {
|
||||
data["forcePasswdChange"] = true
|
||||
}
|
||||
}
|
||||
c.JSON(200, resp.OkData(data))
|
||||
}
|
||||
|
||||
// Router 登录用户路由信息
|
||||
//
|
||||
// GET /router
|
||||
//
|
||||
// @Tags common/authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} object "Response Results"
|
||||
// @Security TokenAuth
|
||||
// @Summary Login User Routing Information
|
||||
// @Description Login User Routing Information
|
||||
// @Router /router [get]
|
||||
func (s AccountController) Router(c *gin.Context) {
|
||||
loginUserId := reqctx.LoginUserToUserID(c)
|
||||
|
||||
// 前端路由,系统管理员拥有所有
|
||||
isSystemUser := config.IsSystemUser(loginUserId)
|
||||
buildMenus := s.accountService.RouteMenus(loginUserId, isSystemUser)
|
||||
// 闭包函数处理多语言
|
||||
language := reqctx.AcceptLanguage(c)
|
||||
var converI18n func(language string, arr *[]systemModelVO.Router)
|
||||
converI18n = func(language string, arr *[]systemModelVO.Router) {
|
||||
for i := range *arr {
|
||||
(*arr)[i].Meta.Title = i18n.TKey(language, (*arr)[i].Meta.Title)
|
||||
if len((*arr)[i].Children) > 0 {
|
||||
converI18n(language, &(*arr)[i].Children)
|
||||
}
|
||||
}
|
||||
}
|
||||
converI18n(language, &buildMenus)
|
||||
|
||||
c.JSON(200, resp.OkData(buildMenus))
|
||||
}
|
||||
80
src/modules/auth/controller/account_ldap.go
Normal file
80
src/modules/auth/controller/account_ldap.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/reqctx"
|
||||
"be.ems/src/framework/resp"
|
||||
"be.ems/src/framework/token"
|
||||
"be.ems/src/modules/auth/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// LDAP LDAP认证登录
|
||||
//
|
||||
// POST /auth/ldap
|
||||
//
|
||||
// @Tags common/authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body object true "Request Param"
|
||||
// @Success 200 {object} object "Response Results"
|
||||
// @Summary System Login
|
||||
// @Description System Login
|
||||
// @Router /auth/ldap [post]
|
||||
func (s AccountController) LDAP(c *gin.Context) {
|
||||
language := reqctx.AcceptLanguage(c)
|
||||
var body model.LoginSourceBody
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
|
||||
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
|
||||
return
|
||||
}
|
||||
|
||||
// 当前请求信息
|
||||
ipaddr, location := reqctx.IPAddrLocation(c)
|
||||
os, browser := reqctx.UaOsBrowser(c)
|
||||
|
||||
// 登录用户信息
|
||||
info, err := s.accountService.ByLDAP(body)
|
||||
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{}
|
||||
|
||||
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))
|
||||
}
|
||||
107
src/modules/auth/controller/account_oauth2.go
Normal file
107
src/modules/auth/controller/account_oauth2.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/reqctx"
|
||||
"be.ems/src/framework/resp"
|
||||
"be.ems/src/framework/token"
|
||||
"be.ems/src/modules/auth/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// OAuth2CodeURL OAuth2认证跳转登录URL
|
||||
//
|
||||
// GET /auth/login/oauth2/authorize
|
||||
//
|
||||
// @Tags common/authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body object true "Request Param"
|
||||
// @Success 200 {object} object "Response Results"
|
||||
// @Summary System Login
|
||||
// @Description System Login
|
||||
// @Router /auth/login/oauth2/authorize [get]
|
||||
func (s AccountController) OAuth2CodeURL(c *gin.Context) {
|
||||
state := c.Query("state")
|
||||
if state == "" {
|
||||
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_CHEACK, "bind err: state is empty"))
|
||||
return
|
||||
}
|
||||
|
||||
redirectURL, err := s.accountService.ByOAuth2CodeURL(state)
|
||||
if err != nil {
|
||||
c.JSON(200, resp.ErrMsg(err.Error()))
|
||||
return
|
||||
}
|
||||
c.Redirect(302, redirectURL)
|
||||
}
|
||||
|
||||
// OAuth2 OAuth2认证登录
|
||||
//
|
||||
// POST /auth/login/oauth2/token
|
||||
//
|
||||
// @Tags common/authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body object true "Request Param"
|
||||
// @Success 200 {object} object "Response Results"
|
||||
// @Summary System Login
|
||||
// @Description System Login
|
||||
// @Router /auth/login/oauth2/token [post]
|
||||
func (s AccountController) OAuth2Token(c *gin.Context) {
|
||||
language := reqctx.AcceptLanguage(c)
|
||||
var body model.LoginSourceOauth2Body
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
|
||||
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
|
||||
return
|
||||
}
|
||||
|
||||
// 当前请求信息
|
||||
ipaddr, location := reqctx.IPAddrLocation(c)
|
||||
os, browser := reqctx.UaOsBrowser(c)
|
||||
|
||||
// 登录用户信息
|
||||
info, err := s.accountService.ByOAuth2(body)
|
||||
if err != nil {
|
||||
s.sysLogLoginService.Insert(
|
||||
body.State, 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{}
|
||||
|
||||
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.State, 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))
|
||||
}
|
||||
80
src/modules/auth/controller/account_smtp.go
Normal file
80
src/modules/auth/controller/account_smtp.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/reqctx"
|
||||
"be.ems/src/framework/resp"
|
||||
"be.ems/src/framework/token"
|
||||
"be.ems/src/modules/auth/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SMTP SMTP认证登录
|
||||
//
|
||||
// POST /auth/smtp
|
||||
//
|
||||
// @Tags common/authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body object true "Request Param"
|
||||
// @Success 200 {object} object "Response Results"
|
||||
// @Summary System Login
|
||||
// @Description System Login
|
||||
// @Router /auth/smtp [post]
|
||||
func (s AccountController) SMTP(c *gin.Context) {
|
||||
language := reqctx.AcceptLanguage(c)
|
||||
var body model.LoginSourceBody
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
errMsgs := fmt.Sprintf("bind err: %s", resp.FormatBindError(err))
|
||||
c.JSON(422, resp.CodeMsg(resp.CODE_PARAM_PARSER, errMsgs))
|
||||
return
|
||||
}
|
||||
|
||||
// 当前请求信息
|
||||
ipaddr, location := reqctx.IPAddrLocation(c)
|
||||
os, browser := reqctx.UaOsBrowser(c)
|
||||
|
||||
// 登录用户信息
|
||||
info, err := s.accountService.BySMTP(body)
|
||||
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{}
|
||||
|
||||
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))
|
||||
}
|
||||
104
src/modules/auth/controller/account_system.go
Normal file
104
src/modules/auth/controller/account_system.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/i18n"
|
||||
"be.ems/src/framework/reqctx"
|
||||
"be.ems/src/framework/resp"
|
||||
"be.ems/src/framework/token"
|
||||
"be.ems/src/modules/auth/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Login 系统登录
|
||||
//
|
||||
// POST /auth/login
|
||||
//
|
||||
// @Tags common/authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body object true "Request Param"
|
||||
// @Success 200 {object} object "Response Results"
|
||||
// @Summary System Login
|
||||
// @Description System Login
|
||||
// @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(resp.CODE_PARAM_PARSER, errMsgs))
|
||||
return
|
||||
}
|
||||
|
||||
// 当前请求信息
|
||||
ipaddr, location := reqctx.IPAddrLocation(c)
|
||||
os, browser := reqctx.UaOsBrowser(c)
|
||||
|
||||
// 校验验证码 根据错误信息,创建系统访问记录
|
||||
if err := s.accountService.ValidateCaptcha(body.Code, body.UUID); 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(i18n.TKey(language, err.Error())))
|
||||
return
|
||||
}
|
||||
|
||||
// 登录用户信息
|
||||
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(info.UserId) {
|
||||
// 强制改密码
|
||||
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
|
||||
}
|
||||
if forcePasswdChange {
|
||||
data["forcePasswdChange"] = true
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
@@ -2,15 +2,8 @@ package model
|
||||
|
||||
// LoginBody 用户登录对象
|
||||
type LoginBody struct {
|
||||
// Username 用户名
|
||||
Username string `json:"username" binding:"required"`
|
||||
|
||||
// Password 用户密码
|
||||
Password string `json:"password" binding:"required"`
|
||||
|
||||
// Code 验证码
|
||||
Code string `json:"code"`
|
||||
|
||||
// UUID 验证码唯一标识
|
||||
UUID string `json:"uuid"`
|
||||
Username string `json:"username" binding:"required"` // Username 用户名
|
||||
Password string `json:"password" binding:"required"` // Password 用户密码
|
||||
Code string `json:"code"` // Code 验证码
|
||||
UUID string `json:"uuid"` // UUID 验证码唯一标识
|
||||
}
|
||||
|
||||
22
src/modules/auth/model/login_source_vo.go
Normal file
22
src/modules/auth/model/login_source_vo.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package model
|
||||
|
||||
// LoginSourceVo 认证源登录对象
|
||||
type LoginSourceVo struct {
|
||||
UID string `json:"uid"` // UID 认证源UID
|
||||
Name string `json:"name"` // Name 认证源名称
|
||||
Type string `json:"type"` // Type 认证源类型
|
||||
Icon string `json:"icon"` // Icon 认证源图标
|
||||
}
|
||||
|
||||
// LoginSourceBody 认证源用户登录对象
|
||||
type LoginSourceBody struct {
|
||||
Username string `json:"username" binding:"required"` // Username 用户名
|
||||
Password string `json:"password" binding:"required"` // Password 用户密码
|
||||
UID string `json:"uid" binding:"required"` // UID 认证源唯一标识
|
||||
}
|
||||
|
||||
// LoginSourceOauth2Body 认证源OAuth2用户登录对象
|
||||
type LoginSourceOauth2Body struct {
|
||||
Code string `json:"code" binding:"required"` // Code 授权码
|
||||
State string `json:"state" binding:"required"` // State 状态-认证源唯一标识
|
||||
}
|
||||
@@ -2,21 +2,10 @@ package model
|
||||
|
||||
// RegisterBody 用户注册对象
|
||||
type RegisterBody struct {
|
||||
// Username 用户名
|
||||
Username string `json:"username" binding:"required"`
|
||||
|
||||
// Password 用户密码
|
||||
Password string `json:"password" binding:"required"`
|
||||
|
||||
// ConfirmPassword 用户确认密码
|
||||
ConfirmPassword string `json:"confirmPassword" binding:"required"`
|
||||
|
||||
// Code 验证码
|
||||
Code string `json:"code"`
|
||||
|
||||
// UUID 验证码唯一标识
|
||||
UUID string `json:"uuid"`
|
||||
|
||||
// UserType 标记用户类型
|
||||
UserType string `json:"userType"`
|
||||
Username string `json:"username" binding:"required"` // Username 用户名
|
||||
Password string `json:"password" binding:"required"` // Password 用户密码
|
||||
ConfirmPassword string `json:"confirmPassword" binding:"required"` // ConfirmPassword 用户确认密码
|
||||
Code string `json:"code"` // Code 验证码
|
||||
UUID string `json:"uuid"` // UUID 验证码唯一标识
|
||||
UserType string `json:"userType"` // UserType 标记用户类型
|
||||
}
|
||||
|
||||
@@ -2,102 +2,32 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/token"
|
||||
"be.ems/src/framework/utils/crypto"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
systemModelVO "be.ems/src/modules/system/model/vo"
|
||||
"be.ems/src/modules/auth/model"
|
||||
systemModel "be.ems/src/modules/system/model"
|
||||
systemService "be.ems/src/modules/system/service"
|
||||
)
|
||||
|
||||
// 实例化服务层 Account 结构体
|
||||
var NewAccount = &Account{
|
||||
sysUserService: systemService.NewSysUser,
|
||||
sysConfigService: systemService.NewSysConfig,
|
||||
sysRoleService: systemService.NewSysRole,
|
||||
sysMenuService: systemService.NewSysMenu,
|
||||
sysUserService: systemService.NewSysUser,
|
||||
sysConfigService: systemService.NewSysConfig,
|
||||
sysRoleService: systemService.NewSysRole,
|
||||
sysMenuService: systemService.NewSysMenu,
|
||||
sysLogSourceService: systemService.NewSysLoginSource,
|
||||
}
|
||||
|
||||
// 账号身份操作服务 服务层处理
|
||||
type Account struct {
|
||||
sysUserService *systemService.SysUser // 用户信息服务
|
||||
sysConfigService *systemService.SysConfig // 参数配置服务
|
||||
sysRoleService *systemService.SysRole // 角色服务
|
||||
sysMenuService *systemService.SysMenu // 菜单服务
|
||||
}
|
||||
|
||||
// ValidateCaptcha 校验验证码
|
||||
func (s *Account) ValidateCaptcha(code, uuid string) error {
|
||||
// 验证码检查,从数据库配置获取验证码开关 true开启,false关闭
|
||||
captchaEnabledStr := s.sysConfigService.FindValueByKey("sys.account.captchaEnabled")
|
||||
if !parse.Boolean(captchaEnabledStr) {
|
||||
return nil
|
||||
}
|
||||
if code == "" || uuid == "" {
|
||||
// 验证码信息错误
|
||||
return fmt.Errorf("captcha.err")
|
||||
}
|
||||
verifyKey := constants.CACHE_CAPTCHA_CODE + ":" + uuid
|
||||
captcha, _ := redis.Get("", verifyKey)
|
||||
if captcha == "" {
|
||||
// 验证码已失效
|
||||
return fmt.Errorf("captcha.errValid")
|
||||
}
|
||||
_ = redis.Del("", verifyKey)
|
||||
if captcha != code {
|
||||
// 验证码错误
|
||||
return fmt.Errorf("captcha.err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ByUsername 登录创建用户信息
|
||||
func (s Account) ByUsername(username, password string) (token.UserInfo, error) {
|
||||
info := token.UserInfo{}
|
||||
|
||||
// 检查密码重试次数
|
||||
retryKey, retryCount, lockTime, err := s.passwordRetryCount(username)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
// 查询用户登录账号
|
||||
sysUser := s.sysUserService.FindByUserName(username)
|
||||
if sysUser.UserName != username {
|
||||
return info, fmt.Errorf("login.errNameOrPasswd")
|
||||
}
|
||||
if sysUser.DelFlag == constants.STATUS_YES {
|
||||
return info, fmt.Errorf("login.errDelFlag")
|
||||
}
|
||||
if sysUser.StatusFlag == constants.STATUS_NO {
|
||||
return info, fmt.Errorf("login.errStatus")
|
||||
}
|
||||
|
||||
// 检验用户密码
|
||||
compareBool := crypto.BcryptCompare(password, sysUser.Password)
|
||||
if compareBool {
|
||||
s.CleanLoginRecordCache(sysUser.UserName) // 清除错误记录次数
|
||||
} else {
|
||||
_ = redis.Set("", retryKey, retryCount+1, lockTime)
|
||||
return info, fmt.Errorf("login.errNameOrPasswd")
|
||||
}
|
||||
|
||||
// 登录用户信息
|
||||
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
|
||||
sysUserService *systemService.SysUser // 用户信息服务
|
||||
sysConfigService *systemService.SysConfig // 参数配置服务
|
||||
sysRoleService *systemService.SysRole // 角色服务
|
||||
sysMenuService *systemService.SysMenu // 菜单服务
|
||||
sysLogSourceService *systemService.SysLoginSource // 认证源
|
||||
}
|
||||
|
||||
// ByUserId 用户ID刷新令牌创建用户信息
|
||||
@@ -140,86 +70,43 @@ func (s Account) UpdateLoginDateAndIP(info token.UserInfo) bool {
|
||||
return s.sysUserService.Update(user) > 0
|
||||
}
|
||||
|
||||
// CleanLoginRecordCache 清除错误记录次数
|
||||
func (s Account) CleanLoginRecordCache(userName string) bool {
|
||||
cacheKey := fmt.Sprintf("%s:%s", constants.CACHE_PWD_ERR_COUNT, userName)
|
||||
hasKey, err := redis.Has("", cacheKey)
|
||||
if hasKey > 0 && err == nil {
|
||||
return redis.Del("", cacheKey) == nil
|
||||
// LoginSource 登录认证源
|
||||
func (s Account) LoginSource() []model.LoginSourceVo {
|
||||
rows := s.sysLogSourceService.FindByActive("")
|
||||
data := make([]model.LoginSourceVo, 0)
|
||||
for _, v := range rows {
|
||||
data = append(data, model.LoginSourceVo{
|
||||
UID: v.UID,
|
||||
Name: v.Name,
|
||||
Type: v.Type,
|
||||
Icon: v.Icon,
|
||||
})
|
||||
}
|
||||
return false
|
||||
return data
|
||||
}
|
||||
|
||||
// passwordRetryCount 密码重试次数
|
||||
func (s Account) passwordRetryCount(userName string) (string, int64, time.Duration, error) {
|
||||
// 从数据库配置获取登录次数和错误锁定时间
|
||||
maxRetryCountStr := s.sysConfigService.FindValueByKey("sys.user.maxRetryCount")
|
||||
lockTimeStr := s.sysConfigService.FindValueByKey("sys.user.lockTime")
|
||||
// 验证登录次数和错误锁定时间
|
||||
maxRetryCount := parse.Number(maxRetryCountStr)
|
||||
lockTime := parse.Number(lockTimeStr)
|
||||
// initLoginSourceUser 初始化登录源用户
|
||||
func (s *Account) initLoginSourceUser(uid, sType, username, password string) systemModel.SysUser {
|
||||
sysUser := systemModel.SysUser{
|
||||
UserName: username,
|
||||
NickName: username, // 昵称使用名称账号
|
||||
Password: password, // 原始密码
|
||||
UserType: sType,
|
||||
UserSource: uid,
|
||||
Sex: "0", // 性别未选择
|
||||
StatusFlag: constants.STATUS_YES, // 账号状态激活
|
||||
DeptId: 101, // 归属部门为根节点
|
||||
CreateBy: sType, // 创建来源
|
||||
}
|
||||
|
||||
// 验证缓存记录次数
|
||||
retryKey := fmt.Sprintf("%s:%s", constants.CACHE_PWD_ERR_COUNT, userName)
|
||||
retryCount, err := redis.Get("", retryKey)
|
||||
if retryCount == "" || err != nil {
|
||||
retryCount = "0"
|
||||
// 新增用户的角色管理
|
||||
sysUser.RoleIds = []int64{5}
|
||||
// 新增用户的岗位管理
|
||||
sysUser.PostIds = []int64{}
|
||||
|
||||
insertId := s.sysUserService.Insert(sysUser)
|
||||
if insertId > 0 {
|
||||
sysUser.UserId = insertId
|
||||
}
|
||||
// 是否超过错误值
|
||||
retryCountInt64 := parse.Number(retryCount)
|
||||
if retryCountInt64 >= int64(maxRetryCount) {
|
||||
// msg := fmt.Sprintf("密码输入错误 %d 次,帐户锁定 %d 分钟", maxRetryCount, lockTime)
|
||||
msg := fmt.Errorf("login.errRetryPasswd") // 密码输入错误多次,帐户已被锁定
|
||||
return retryKey, retryCountInt64, time.Duration(lockTime) * time.Minute, fmt.Errorf("%s", msg)
|
||||
}
|
||||
return retryKey, retryCountInt64, time.Duration(lockTime) * time.Minute, nil
|
||||
}
|
||||
|
||||
// PasswordCountOrExpireTime 首次登录或密码过期时间
|
||||
func (s Account) PasswordCountOrExpireTime(loginCount, passwordUpdateTime int64) (bool, error) {
|
||||
forcePasswdChange := false
|
||||
// 从数据库配置获取-首次登录密码修改
|
||||
fristPasswdChangeStr := s.sysConfigService.FindValueByKey("sys.user.fristPasswdChange")
|
||||
if parse.Boolean(fristPasswdChangeStr) {
|
||||
forcePasswdChange = loginCount < 1 || passwordUpdateTime == 0
|
||||
}
|
||||
|
||||
// 非首次登录,判断密码是否过期
|
||||
if !forcePasswdChange {
|
||||
alert, err := s.sysUserService.ValidatePasswordExpireTime(passwordUpdateTime)
|
||||
if err != nil {
|
||||
return alert, err
|
||||
}
|
||||
forcePasswdChange = alert
|
||||
}
|
||||
return forcePasswdChange, nil
|
||||
}
|
||||
|
||||
// RoleAndMenuPerms 角色和菜单数据权限
|
||||
func (s Account) RoleAndMenuPerms(userId int64, isSystemUser bool) ([]string, []string) {
|
||||
if isSystemUser {
|
||||
return []string{constants.SYS_ROLE_SYSTEM_KEY}, []string{constants.SYS_PERMISSION_SYSTEM}
|
||||
}
|
||||
// 角色key
|
||||
var roleGroup []string
|
||||
roles := s.sysRoleService.FindByUserId(userId)
|
||||
for _, role := range roles {
|
||||
roleGroup = append(roleGroup, role.RoleKey)
|
||||
}
|
||||
// 菜单权限key
|
||||
perms := s.sysMenuService.FindPermsByUserId(userId)
|
||||
return parse.RemoveDuplicates(roleGroup), parse.RemoveDuplicates(perms)
|
||||
}
|
||||
|
||||
// RouteMenus 前端路由所需要的菜单
|
||||
func (s Account) RouteMenus(userId int64, isSystemUser bool) []systemModelVO.Router {
|
||||
var buildMenus []systemModelVO.Router
|
||||
if isSystemUser {
|
||||
menus := s.sysMenuService.BuildTreeMenusByUserId(0)
|
||||
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
|
||||
} else {
|
||||
menus := s.sysMenuService.BuildTreeMenusByUserId(userId)
|
||||
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
|
||||
}
|
||||
return buildMenus
|
||||
return s.sysUserService.FindByUserName(username, sType, uid)
|
||||
}
|
||||
|
||||
36
src/modules/auth/service/account_info.go
Normal file
36
src/modules/auth/service/account_info.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
systemModelVO "be.ems/src/modules/system/model/vo"
|
||||
)
|
||||
|
||||
// RoleAndMenuPerms 角色和菜单数据权限
|
||||
func (s Account) RoleAndMenuPerms(userId int64, isSystemUser bool) ([]string, []string) {
|
||||
if isSystemUser {
|
||||
return []string{constants.SYS_ROLE_SYSTEM_KEY}, []string{constants.SYS_PERMISSION_SYSTEM}
|
||||
}
|
||||
// 角色key
|
||||
var roleGroup []string
|
||||
roles := s.sysRoleService.FindByUserId(userId)
|
||||
for _, role := range roles {
|
||||
roleGroup = append(roleGroup, role.RoleKey)
|
||||
}
|
||||
// 菜单权限key
|
||||
perms := s.sysMenuService.FindPermsByUserId(userId)
|
||||
return parse.RemoveDuplicates(roleGroup), parse.RemoveDuplicates(perms)
|
||||
}
|
||||
|
||||
// RouteMenus 前端路由所需要的菜单
|
||||
func (s Account) RouteMenus(userId int64, isSystemUser bool) []systemModelVO.Router {
|
||||
var buildMenus []systemModelVO.Router
|
||||
if isSystemUser {
|
||||
menus := s.sysMenuService.BuildTreeMenusByUserId(0)
|
||||
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
|
||||
} else {
|
||||
menus := s.sysMenuService.BuildTreeMenusByUserId(userId)
|
||||
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
|
||||
}
|
||||
return buildMenus
|
||||
}
|
||||
108
src/modules/auth/service/account_ldap.go
Normal file
108
src/modules/auth/service/account_ldap.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/token"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
"be.ems/src/modules/auth/model"
|
||||
systemModelVo "be.ems/src/modules/system/model/vo"
|
||||
)
|
||||
|
||||
// ByLDAP 登录创建用户信息
|
||||
func (s *Account) ByLDAP(body model.LoginSourceBody) (token.UserInfo, error) {
|
||||
info := token.UserInfo{}
|
||||
rows := s.sysLogSourceService.FindByActive(body.UID)
|
||||
if len(rows) != 1 {
|
||||
return info, fmt.Errorf("ldap auth source not exist")
|
||||
}
|
||||
item := rows[0]
|
||||
if item.Config == "" {
|
||||
return info, fmt.Errorf("ldap auth source config is empty")
|
||||
}
|
||||
var source systemModelVo.SysLoginSourceLDAP
|
||||
if err := json.Unmarshal([]byte(item.Config), &source); err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
// 校验LDAP用户
|
||||
err := ldapAuth(source, body.Username, body.Password)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
// 查询用户登录账号
|
||||
sysUser := s.sysUserService.FindByUserName(body.Username, item.Type, item.UID)
|
||||
if sysUser.UserId == 0 || sysUser.UserName == "" {
|
||||
sysUser = s.initLoginSourceUser(item.UID, item.Type, body.Username, body.Password)
|
||||
}
|
||||
if sysUser.UserId == 0 || sysUser.UserName != body.Username {
|
||||
return info, fmt.Errorf("login.errNameOrPasswd")
|
||||
}
|
||||
if sysUser.DelFlag == constants.STATUS_YES {
|
||||
return info, fmt.Errorf("login.errDelFlag")
|
||||
}
|
||||
if sysUser.StatusFlag == constants.STATUS_NO {
|
||||
return info, fmt.Errorf("login.errStatus")
|
||||
}
|
||||
|
||||
// 登录用户信息
|
||||
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
|
||||
}
|
||||
|
||||
// ldapAuth 校验LDAP用户
|
||||
func ldapAuth(source systemModelVo.SysLoginSourceLDAP, username, password string) error {
|
||||
// 连接LDAP
|
||||
l, err := ldap.DialURL(source.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
// 绑定DN校验
|
||||
if source.BindDN != "" && source.BindPassword != "" {
|
||||
if err := l.Bind(source.BindDN, source.BindPassword); err != nil {
|
||||
return fmt.Errorf("ldap user bind %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索用户
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
source.BaseDN,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf(source.UserFilter, ldap.EscapeFilter(username)),
|
||||
[]string{"dn", "uid"},
|
||||
nil,
|
||||
)
|
||||
sr, err := l.Search(searchRequest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ldap user search %s", err)
|
||||
}
|
||||
// for _, entry := range sr.Entries {
|
||||
// fmt.Printf("%s ==== %v\n", entry.DN, entry.GetAttributeValue("uid"))
|
||||
// }
|
||||
if len(sr.Entries) != 1 {
|
||||
return fmt.Errorf("ldap user does not exist or too many entries returned")
|
||||
}
|
||||
|
||||
// 校验密码
|
||||
if err = l.Bind(sr.Entries[0].DN, password); err != nil {
|
||||
return fmt.Errorf("ldap user bind %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
168
src/modules/auth/service/account_oauth2.go
Normal file
168
src/modules/auth/service/account_oauth2.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/token"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
"be.ems/src/modules/auth/model"
|
||||
systemModelVo "be.ems/src/modules/system/model/vo"
|
||||
)
|
||||
|
||||
// ByOAuth2CodeURL 获取OAuth2登录URL
|
||||
func (s *Account) ByOAuth2CodeURL(state string) (string, error) {
|
||||
rows := s.sysLogSourceService.FindByActive(state)
|
||||
if len(rows) != 1 {
|
||||
return "", fmt.Errorf("oauth2 auth source not exist")
|
||||
}
|
||||
item := rows[0]
|
||||
if item.Config == "" {
|
||||
return "", fmt.Errorf("oauth2 auth source config is empty")
|
||||
}
|
||||
var source systemModelVo.SysLoginSourceOAuth2
|
||||
json.Unmarshal([]byte(item.Config), &source)
|
||||
|
||||
conf := oauth2.Config{
|
||||
ClientID: source.ClientID,
|
||||
ClientSecret: source.ClientSecret,
|
||||
RedirectURL: source.RedirectURL,
|
||||
Scopes: source.Scopes,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: source.AuthURL,
|
||||
TokenURL: source.TokenURL,
|
||||
},
|
||||
}
|
||||
return conf.AuthCodeURL(state, oauth2.AccessTypeOffline), nil
|
||||
}
|
||||
|
||||
// ByOAuth2 登录创建用户信息
|
||||
func (s *Account) ByOAuth2(body model.LoginSourceOauth2Body) (token.UserInfo, error) {
|
||||
info := token.UserInfo{}
|
||||
rows := s.sysLogSourceService.FindByActive(body.State)
|
||||
if len(rows) != 1 {
|
||||
return info, fmt.Errorf("oauth2 auth source not exist")
|
||||
}
|
||||
item := rows[0]
|
||||
if item.Config == "" {
|
||||
return info, fmt.Errorf("oauth2 auth source config is empty")
|
||||
}
|
||||
var source systemModelVo.SysLoginSourceOAuth2
|
||||
if err := json.Unmarshal([]byte(item.Config), &source); err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
// 校验OAuth2用户
|
||||
account, err := oauth2Auth(source, body.Code)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
// 查询用户登录账号
|
||||
sysUser := s.sysUserService.FindByUserName(account, item.Type, item.UID)
|
||||
if sysUser.UserId == 0 || sysUser.UserName == "" {
|
||||
sysUser = s.initLoginSourceUser(item.UID, item.Type, account, account)
|
||||
}
|
||||
if sysUser.UserId == 0 || sysUser.UserName != account {
|
||||
return info, fmt.Errorf("login.errNameOrPasswd")
|
||||
}
|
||||
if sysUser.DelFlag == constants.STATUS_YES {
|
||||
return info, fmt.Errorf("login.errDelFlag")
|
||||
}
|
||||
if sysUser.StatusFlag == constants.STATUS_NO {
|
||||
return info, fmt.Errorf("login.errStatus")
|
||||
}
|
||||
|
||||
// 登录用户信息
|
||||
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
|
||||
}
|
||||
|
||||
// oauth2Auth 校验OAuth2用户
|
||||
func oauth2Auth(source systemModelVo.SysLoginSourceOAuth2, code string) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
conf := oauth2.Config{
|
||||
ClientID: source.ClientID,
|
||||
ClientSecret: source.ClientSecret,
|
||||
RedirectURL: source.RedirectURL,
|
||||
Scopes: source.Scopes,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: source.AuthURL,
|
||||
TokenURL: source.TokenURL,
|
||||
},
|
||||
}
|
||||
token, err := conf.Exchange(ctx, code)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 使用token创建HTTP客户端 请求用户信息
|
||||
resp, err := conf.Client(ctx, token).Get(source.UserURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
// 解析用户信息
|
||||
var userInfo map[string]any
|
||||
if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 读取嵌套数据
|
||||
value, found := getValueByPath(userInfo, source.AccountField)
|
||||
if !found {
|
||||
return "", fmt.Errorf("oauth2 auth source account field not exist")
|
||||
}
|
||||
return fmt.Sprintf("%v", value), nil
|
||||
}
|
||||
|
||||
// getValueByPath 从嵌套的 map[string]any 获取嵌套键对应的值
|
||||
func getValueByPath(data map[string]any, path string) (any, bool) {
|
||||
keys := strings.Split(path, ".") // 按照 "." 拆分路径
|
||||
return getValue(data, keys)
|
||||
}
|
||||
|
||||
// getValue 是递归查找嵌套 map 的函数
|
||||
func getValue(data map[string]any, keys []string) (any, bool) {
|
||||
if len(keys) == 0 {
|
||||
return data, false
|
||||
}
|
||||
|
||||
// 获取当前键
|
||||
key := keys[0]
|
||||
|
||||
// 获取当前键的值
|
||||
val, ok := data[key]
|
||||
if !ok {
|
||||
return nil, false // 找不到键,返回 false
|
||||
}
|
||||
|
||||
// 如果还有嵌套键,继续查找
|
||||
if len(keys) > 1 {
|
||||
// 递归查找嵌套 map
|
||||
if reflect.TypeOf(val).Kind() == reflect.Map {
|
||||
// 将 `any` 转换为 `map[string]any` 类型
|
||||
if nestedMap, ok := val.(map[string]any); ok {
|
||||
return getValue(nestedMap, keys[1:])
|
||||
}
|
||||
}
|
||||
}
|
||||
return val, true
|
||||
}
|
||||
88
src/modules/auth/service/account_smtp.go
Normal file
88
src/modules/auth/service/account_smtp.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/wneessen/go-mail"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/token"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
"be.ems/src/modules/auth/model"
|
||||
systemModelVo "be.ems/src/modules/system/model/vo"
|
||||
)
|
||||
|
||||
// BySMTP 登录创建用户信息
|
||||
func (s *Account) BySMTP(body model.LoginSourceBody) (token.UserInfo, error) {
|
||||
info := token.UserInfo{}
|
||||
rows := s.sysLogSourceService.FindByActive(body.UID)
|
||||
if len(rows) != 1 {
|
||||
return info, fmt.Errorf("smtp auth source not exist")
|
||||
}
|
||||
item := rows[0]
|
||||
if item.Config == "" {
|
||||
return info, fmt.Errorf("smtp auth source config is empty")
|
||||
}
|
||||
var source systemModelVo.SysLoginSourceSMTP
|
||||
if err := json.Unmarshal([]byte(item.Config), &source); err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
// 校验SMTP用户
|
||||
err := smtpAuth(source, body.Username, body.Password)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
// 查询用户登录账号
|
||||
sysUser := s.sysUserService.FindByUserName(body.Username, item.Type, item.UID)
|
||||
if sysUser.UserId == 0 || sysUser.UserName == "" {
|
||||
sysUser = s.initLoginSourceUser(item.UID, item.Type, body.Username, body.Password)
|
||||
}
|
||||
if sysUser.UserId == 0 || sysUser.UserName != body.Username {
|
||||
return info, fmt.Errorf("login.errNameOrPasswd")
|
||||
}
|
||||
if sysUser.DelFlag == constants.STATUS_YES {
|
||||
return info, fmt.Errorf("login.errDelFlag")
|
||||
}
|
||||
if sysUser.StatusFlag == constants.STATUS_NO {
|
||||
return info, fmt.Errorf("login.errStatus")
|
||||
}
|
||||
|
||||
// 登录用户信息
|
||||
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
|
||||
}
|
||||
|
||||
// smtpAuth 校验SMTP用户
|
||||
func smtpAuth(source systemModelVo.SysLoginSourceSMTP, username, password string) error {
|
||||
client, err := mail.NewClient(source.Host,
|
||||
mail.WithSMTPAuth(mail.SMTPAuthAutoDiscover),
|
||||
mail.WithUsername(username),
|
||||
mail.WithPort(source.Port),
|
||||
mail.WithPassword(password),
|
||||
mail.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create mail client %s", err)
|
||||
}
|
||||
// 连接到邮件SMTP服务器
|
||||
if err = client.DialWithContext(context.Background()); err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
return nil
|
||||
}
|
||||
138
src/modules/auth/service/account_sysstem.go
Normal file
138
src/modules/auth/service/account_sysstem.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"be.ems/src/framework/config"
|
||||
"be.ems/src/framework/constants"
|
||||
"be.ems/src/framework/database/redis"
|
||||
"be.ems/src/framework/token"
|
||||
"be.ems/src/framework/utils/crypto"
|
||||
"be.ems/src/framework/utils/parse"
|
||||
)
|
||||
|
||||
// ValidateCaptcha 校验验证码
|
||||
func (s Account) ValidateCaptcha(code, uuid string) error {
|
||||
// 验证码检查,从数据库配置获取验证码开关 true开启,false关闭
|
||||
captchaEnabledStr := s.sysConfigService.FindValueByKey("sys.account.captchaEnabled")
|
||||
if !parse.Boolean(captchaEnabledStr) {
|
||||
return nil
|
||||
}
|
||||
if code == "" || uuid == "" {
|
||||
// 验证码信息错误
|
||||
return fmt.Errorf("captcha.err")
|
||||
}
|
||||
verifyKey := constants.CACHE_CAPTCHA_CODE + ":" + uuid
|
||||
captcha, _ := redis.Get("", verifyKey)
|
||||
if captcha == "" {
|
||||
// 验证码已失效
|
||||
return fmt.Errorf("captcha.errValid")
|
||||
}
|
||||
_ = redis.Del("", verifyKey)
|
||||
if captcha != code {
|
||||
// 验证码错误
|
||||
return fmt.Errorf("captcha.err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ByUsername 登录创建用户信息
|
||||
func (s Account) ByUsername(username, password string) (token.UserInfo, error) {
|
||||
info := token.UserInfo{}
|
||||
|
||||
// 检查密码重试次数
|
||||
retryKey, retryCount, lockTime, err := s.passwordRetryCount(username)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
// 查询用户登录账号
|
||||
sysUser := s.sysUserService.FindByUserName(username, "System", "#")
|
||||
if sysUser.UserName != username {
|
||||
return info, fmt.Errorf("login.errNameOrPasswd")
|
||||
}
|
||||
if sysUser.DelFlag == constants.STATUS_YES {
|
||||
return info, fmt.Errorf("login.errDelFlag")
|
||||
}
|
||||
if sysUser.StatusFlag == constants.STATUS_NO {
|
||||
return info, fmt.Errorf("login.errStatus")
|
||||
}
|
||||
|
||||
// 检验用户密码
|
||||
compareBool := crypto.BcryptCompare(password, sysUser.Password)
|
||||
if compareBool {
|
||||
s.CleanLoginRecordCache(sysUser.UserName) // 清除错误记录次数
|
||||
} else {
|
||||
_ = redis.Set("", retryKey, retryCount+1, lockTime)
|
||||
return info, fmt.Errorf("login.errNameOrPasswd")
|
||||
}
|
||||
|
||||
// 登录用户信息
|
||||
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
|
||||
}
|
||||
|
||||
// CleanLoginRecordCache 清除错误记录次数
|
||||
func (s Account) CleanLoginRecordCache(userName string) bool {
|
||||
cacheKey := fmt.Sprintf("%s:%s", constants.CACHE_PWD_ERR_COUNT, userName)
|
||||
hasKey, err := redis.Has("", cacheKey)
|
||||
if hasKey > 0 && err == nil {
|
||||
return redis.Del("", cacheKey) == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// passwordRetryCount 密码重试次数
|
||||
func (s Account) passwordRetryCount(userName string) (string, int64, time.Duration, error) {
|
||||
// 从数据库配置获取登录次数和错误锁定时间
|
||||
maxRetryCountStr := s.sysConfigService.FindValueByKey("sys.user.maxRetryCount")
|
||||
lockTimeStr := s.sysConfigService.FindValueByKey("sys.user.lockTime")
|
||||
// 验证登录次数和错误锁定时间
|
||||
maxRetryCount := parse.Number(maxRetryCountStr)
|
||||
lockTime := parse.Number(lockTimeStr)
|
||||
|
||||
// 验证缓存记录次数
|
||||
retryKey := fmt.Sprintf("%s:%s", constants.CACHE_PWD_ERR_COUNT, userName)
|
||||
retryCount, err := redis.Get("", retryKey)
|
||||
if retryCount == "" || err != nil {
|
||||
retryCount = "0"
|
||||
}
|
||||
// 是否超过错误值
|
||||
retryCountInt64 := parse.Number(retryCount)
|
||||
if retryCountInt64 >= int64(maxRetryCount) {
|
||||
// msg := fmt.Sprintf("密码输入错误 %d 次,帐户锁定 %d 分钟", maxRetryCount, lockTime)
|
||||
msg := fmt.Errorf("login.errRetryPasswd") // 密码输入错误多次,帐户已被锁定
|
||||
return retryKey, retryCountInt64, time.Duration(lockTime) * time.Minute, fmt.Errorf("%s", msg)
|
||||
}
|
||||
return retryKey, retryCountInt64, time.Duration(lockTime) * time.Minute, nil
|
||||
}
|
||||
|
||||
// PasswordCountOrExpireTime 首次登录或密码过期时间
|
||||
func (s Account) PasswordCountOrExpireTime(loginCount, passwordUpdateTime int64) (bool, error) {
|
||||
forcePasswdChange := false
|
||||
// 从数据库配置获取-首次登录密码修改
|
||||
fristPasswdChangeStr := s.sysConfigService.FindValueByKey("sys.user.fristPasswdChange")
|
||||
if parse.Boolean(fristPasswdChangeStr) {
|
||||
forcePasswdChange = loginCount < 1 || passwordUpdateTime == 0
|
||||
}
|
||||
|
||||
// 非首次登录,判断密码是否过期
|
||||
if !forcePasswdChange {
|
||||
alert, err := s.sysUserService.ValidatePasswordExpireTime(passwordUpdateTime)
|
||||
if err != nil {
|
||||
return alert, err
|
||||
}
|
||||
forcePasswdChange = alert
|
||||
}
|
||||
return forcePasswdChange, nil
|
||||
}
|
||||
@@ -61,12 +61,14 @@ func (s Register) ByUserName(username, password string) (int64, error) {
|
||||
|
||||
sysUser := systemModel.SysUser{
|
||||
UserName: username,
|
||||
NickName: username, // 昵称使用名称账号
|
||||
Password: password, // 原始密码
|
||||
NickName: username, // 昵称使用名称账号
|
||||
Password: password, // 原始密码
|
||||
UserType: "System",
|
||||
UserSource: "#",
|
||||
Sex: "0", // 性别未选择
|
||||
StatusFlag: constants.STATUS_YES, // 账号状态激活
|
||||
DeptId: 100, // 归属部门为根节点
|
||||
CreateBy: "register", // 创建来源
|
||||
DeptId: 101, // 归属部门为根节点
|
||||
CreateBy: "System", // 创建来源
|
||||
}
|
||||
|
||||
// 新增用户的角色管理
|
||||
|
||||
Reference in New Issue
Block a user