From 6f69d2b4a05e4a4e9ee3e2814245d31100bd9f55 Mon Sep 17 00:00:00 2001 From: HuangZhi Date: Mon, 10 Jul 2023 17:07:13 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E8=B4=A6=E6=88=B7=E5=AE=A1?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bean/entity/system_user.go | 53 ++-- src/bean/vo/request/system_user.go | 1 + src/bean/vo/response/log_management.go | 2 +- src/bean/vo/response/system_menu.go | 1 + src/common/client/redis.go | 17 ++ src/controller/log_management.go | 79 +++--- src/controller/login.go | 84 +++++- src/router/logmanagementrouter.go | 13 +- src/service/access_rule.go | 139 ++++++++++ src/service/log_management.go | 353 +++++++++---------------- src/service/login.go | 146 ++++++++-- src/service/system_user.go | 44 ++- src/util/common.go | 79 ++++++ 13 files changed, 687 insertions(+), 324 deletions(-) create mode 100644 src/service/access_rule.go diff --git a/src/bean/entity/system_user.go b/src/bean/entity/system_user.go index 97c257d..6b70e55 100644 --- a/src/bean/entity/system_user.go +++ b/src/bean/entity/system_user.go @@ -5,29 +5,38 @@ import ( ) type SystemUser struct { - Id int `json:"id" xorm:"pk autoincr" ` // id - Name string `json:"name" ` // 名称 - SystemAccount string `json:"system_account" ` // 账号 - OrganizationId string `json:"organization_id" xorm:"organization_id"` // 所属组织 - Password string `json:"password"` // 密码 - State int `json:"state" xorm:"state"` // 状态0禁用1启用 - Logo string `json:"logo" xorm:"logo"` // 头像logo - CreatedBy int `json:"created_by" ` // 创建者 - CreatedTime time.Time `json:"created_time" xorm:"created" ` // 创建时间 - UpdatedBy int `json:"updated_by" ` // 更新者 - UpdatedTime time.Time `json:"updated_time" xorm:"updated" ` // 更新时间 - IsDeleted int `json:"is_deleted" xorm:"is_deleted"` // 是否删除 - Phone string `json:"phone" xorm:"phone"` // 手机号 + Id int `json:"id" xorm:"pk autoincr" ` // id + Name string `json:"name" ` // 名称 + SystemAccount string `json:"system_account" ` // 账号 + OrganizationId string `json:"organization_id" xorm:"organization_id"` // 所属组织 + Password string `json:"password"` // 密码 + State int `json:"state" xorm:"state"` // 状态0禁用1启用 + Logo string `json:"logo" xorm:"logo"` // 头像logo + CreatedBy int `json:"created_by" ` // 创建者 + CreatedTime time.Time `json:"created_time" xorm:"created" ` // 创建时间 + UpdatedBy int `json:"updated_by" ` // 更新者 + UpdatedTime time.Time `json:"updated_time" xorm:"updated" ` // 更新时间 + IsDeleted int `json:"is_deleted" xorm:"is_deleted"` // 是否删除 + Phone string `json:"phone" xorm:"phone"` // 手机号 + PwdLevel int `json:"pwd_level"` // 密码强度1低2中3高 + PwdForceStatus int `json:"pwd_force_status"` // 密码强制修改状态0否1是 + LastAccessTime time.Time `json:"last_access_time" xorm:"last_access_time"` // 用户最后访问时间 + PwdUpdatedTime time.Time `json:"pwd_updated_time"` // 密码修改时间 } type SystemUserInfo struct { - Id int `json:"id" xorm:"pk autoincr" ` // id - Name string `json:"name" ` // 名称 - SystemAccount string `json:"system_account" ` // 账号 - OrganizationId string `json:"organization_id" xorm:"organization_id"` // 所属组织 - Password string `json:"password,omitempty"` // 密码 - State int `json:"state" xorm:"state"` // 状态0禁用1启用 - Logo string `json:"logo" xorm:"logo"` // 头像logo - IsDeleted int `json:"is_deleted" xorm:"is_deleted"` // 是否删除 - Phone string `json:"phone" xorm:"phone"` // 手机号 + Id int `json:"id" xorm:"pk autoincr" ` // id + Name string `json:"name" ` // 名称 + SystemAccount string `json:"system_account" ` // 账号 + OrganizationId string `json:"organization_id" xorm:"organization_id"` // 所属组织 + Password string `json:"password,omitempty"` // 密码 + State int `json:"state" xorm:"state"` // 状态0禁用1启用 + Logo string `json:"logo" xorm:"logo"` // 头像logo + IsDeleted int `json:"is_deleted" xorm:"is_deleted"` // 是否删除 + Phone string `json:"phone" xorm:"phone"` // 手机号 + PwdLevel int `json:"pwd_level"` // 密码强度1低2中3高 + PwdForceStatus int `json:"pwd_force_status"` // 密码强制修改状态0否1是 + LastAccessTime time.Time `json:"last_access_time" xorm:"last_access_time"` // 用户最后访问时间 + PwdUpdatedTime time.Time `json:"pwd_updated_time"` // 密码修改时间 + OrgName string `json:"org_name"` // 组织机构名称 } diff --git a/src/bean/vo/request/system_user.go b/src/bean/vo/request/system_user.go index 68e404d..a30bb3d 100644 --- a/src/bean/vo/request/system_user.go +++ b/src/bean/vo/request/system_user.go @@ -18,6 +18,7 @@ type CreateSystemUserReq struct { CreatedBy string `json:"created_by"` // 用户创建人 UpdatedBy string `json:"updated_by"` // 用户更新人 IsAdmin int `json:"is_admin"` // 用户类型 + } type UpdateSystemUserReq struct { diff --git a/src/bean/vo/response/log_management.go b/src/bean/vo/response/log_management.go index 337e102..77cc6ca 100644 --- a/src/bean/vo/response/log_management.go +++ b/src/bean/vo/response/log_management.go @@ -9,7 +9,7 @@ type LogManagementRep struct { OrganizationId string `json:"organization_id" xorm:"organization_id"` // 所属组织 Name string `json:"name"` // 所属组织名称 SystemAccount string `json:"system_account" xorm:"system_account"` // 系统账号 - ContactPhone string `json:"contact_phone" xorm:"contact_phone"` // 联系人电话 + Phone string `json:"phone" xorm:"phone"` // 联系人电话 //IsAdmin string `json:"is_admin" xorm:"is_admin"` // 用户类型 (1.业务系统账号 2.组织管理员账号 3.平台用户账号 4.超级管理员) LastAccessTimes jsontime.Time `json:"-" xorm:"last_access_time"` // 用户最后访问时间 LastAccessTime jsontime.Time `json:"last_access_time" xorm:"-"` // 用户最后访问时间 diff --git a/src/bean/vo/response/system_menu.go b/src/bean/vo/response/system_menu.go index 750f410..ebba72e 100644 --- a/src/bean/vo/response/system_menu.go +++ b/src/bean/vo/response/system_menu.go @@ -40,5 +40,6 @@ type SystemMenuTreePer struct { MenuId string `json:"menu_id" xorm:"menu_id"` //菜单id PMenuId string `json:"p_menu_id" xorm:"p_menu_id"` //上级菜单id SystemType string `json:"system_type"` //系统类型 + Source string `json:"source" xorm:"source"` //源 Children []SystemMenuTreePer `json:"children" xorm:"-"` } diff --git a/src/common/client/redis.go b/src/common/client/redis.go index 9bd0e23..5bb8916 100644 --- a/src/common/client/redis.go +++ b/src/common/client/redis.go @@ -121,3 +121,20 @@ func (r Redis) DelList(keys []string) error { bmd := r.Conn.Del(keys...) return bmd.Err() } + +func (r Redis) LPush(key string, value interface{}) error { + key = fmt.Sprintf("%s-%s", strings.ToUpper(r.Prefix), strings.ToUpper(key)) + bmd := r.Conn.LPush(strings.ToUpper(key), value) + return bmd.Err() +} + +func (r Redis) LRange(key string) (str []string, err error) { + key = fmt.Sprintf("%s-%s", strings.ToUpper(r.Prefix), strings.ToUpper(key)) + return r.Conn.LRange(key, 0, -1).Result() +} + +func (r Redis) LRem(key string, value interface{}) error { + key = fmt.Sprintf("%s-%s", strings.ToUpper(r.Prefix), strings.ToUpper(key)) + bmd := r.Conn.LRem(key, 0, value) + return bmd.Err() +} diff --git a/src/controller/log_management.go b/src/controller/log_management.go index b5add52..6ceef4c 100644 --- a/src/controller/log_management.go +++ b/src/controller/log_management.go @@ -111,44 +111,41 @@ func LogUserBehaviorDelete(c *gin.Context) { SendJsonResponse(c, nil, "删除成功") } -//用户账户审计列表 -//func LogUserAccountAuditList(c *gin.Context) { -// params := request.LogManagementListReq{} -// // 绑定分页数据 -// if err := c.ShouldBindQuery(¶ms); err != nil { -// SendJsonResponse(c, res.ParamsParserError.ErrorDetail(err), nil) -// return -// } -// // 分页数据初始化 limit page Offset -// params.PageInfo = params.PageInfo.InitPage() -// svc := service.LogManagement{User: util.GetContextUser(c)} -// list, count, err := svc.LogUserAccountAuditList(¶ms) -// if err != nil { -// SendJsonResponse(c, err, nil) -// return -// } -// SendJsonPageResponse(c, err, list, count) -//} -// -//// 用户账户审计列表导出LogUserAccountAuditExport -//func LogUserAccountAuditExport(c *gin.Context) { -// params := request.LogManagementListReq{} -// // 绑定分页数据 -// if err := c.ShouldBindQuery(¶ms); err != nil { -// SendJsonResponse(c, res.ParamsParserError.ErrorDetail(err), nil) -// return -// } -// // 分页数据初始化 limit page Offset -// params.PageInfo = params.PageInfo.InitPage() -// svc := service.LogManagement{User: util.GetContextUser(c)} -// file, fileName, err := svc.LogUserAccountAuditExport(¶ms) -// if err != nil { -// SendJsonResponse(c, err, nil) -// return -// } -// c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName)) //fmt.Sprintf("attachment; filename=%s", filename)对下载的文件重命名 -// c.Writer.Header().Add("Content-Type", "application/octet-stream") -// c.Writer.Header().Add("Content-Transfer-Encoding", "binary") -// _ = file.Write(c.Writer) -// -//} +// 用户账户审计列表 +func LogUserAccountAuditList(c *gin.Context) { + params := request.LogManagementListReq{} + // 绑定分页数据 + if err := c.ShouldBindQuery(¶ms); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + svc := service.LogManagement{User: header.GetUser(c)} + list, count, err := svc.LogUserAccountAuditList(¶ms) + if err != nil { + SendJsonResponse(c, err, nil) + return + } + SendJsonPageResponse(c, err, list, count) +} + +// 用户账户审计列表导出LogUserAccountAuditExport +func LogUserAccountAuditExport(c *gin.Context) { + params := request.LogManagementListReq{} + // 绑定分页数据 + if err := c.ShouldBindQuery(¶ms); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + // 分页数据初始化 limit page Offset + svc := service.LogManagement{User: header.GetUser(c)} + file, fileName, err := svc.LogUserAccountAuditExport(¶ms) + if err != nil { + SendJsonResponse(c, err, nil) + return + } + c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName)) //fmt.Sprintf("attachment; filename=%s", filename)对下载的文件重命名 + c.Writer.Header().Add("Content-Type", "application/octet-stream") + c.Writer.Header().Add("Content-Transfer-Encoding", "binary") + _ = file.Write(c.Writer) + +} diff --git a/src/controller/login.go b/src/controller/login.go index d26ec56..4775b93 100644 --- a/src/controller/login.go +++ b/src/controller/login.go @@ -1,6 +1,7 @@ package controller import ( + "errors" vd "github.com/bytedance/go-tagexpr/validator" "github.com/gin-gonic/gin" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" @@ -8,9 +9,9 @@ import ( "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/service" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" - "go.uber.org/zap" "strconv" + "time" ) func UserLogin(c *gin.Context) { @@ -48,7 +49,88 @@ func UserLogin(c *gin.Context) { SendJsonResponse(c, resp.OK, msg) } +func UserLoginV2(c *gin.Context) { + userReq := request.UserReq{} + err := c.BindJSON(&userReq) + if err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + // 参数检测 + if err = vd.Validate(userReq); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + + loginInf := service.UserSvc{SystemAccount: userReq.SystemAccount} + userInfo, err := loginInf.GetUserInfo("") + if err != nil { + SendJsonResponse(c, err, nil) + return + } + + // 获取系统配置信息 + op := service.SystemOptionsSvc{} + config, err := op.GetSystemOptions() + if err != nil { + SendJsonResponse(c, err, nil) + return + } + // 未开启是否启用登录配置时 只进行密码正确性校验 (admin 始终只进行密码校验) + + upperMd5Pass, err := service.SolvePassword(userInfo.Id, userReq.Password) + if err != nil { + conf.Logger.Error("加密错误", zap.Error(err)) + SendJsonResponse(c, err, nil) + return + } + if upperMd5Pass != userInfo.Password { + if config.LoginConfigState == 1 { + err := loginInf.UserLock(userInfo.Id, userInfo.Password, config) + if err != nil { + SendJsonResponse(c, err, nil) + return + } + err = errors.New("密码错误") + SendJsonResponse(c, err, nil) + return + } + loginInf.UserUnLock(userInfo.Id) + + // TODO STEP2 用户和ip没有加入到访问规则登录不上提醒 + if config.AccessRuleState == 1 { + ip := util.RemoteIp(c.Request) + svc := service.AccessRuleSvc{} + if errCode := svc.CheckIp(ip, userInfo.Id); errCode != nil { + SendJsonResponse(c, errCode, nil) + return + } + } + // 密码失效提醒 + subDays := util.SubDays(time.Now(), userInfo.PwdUpdatedTime) + if config.PwdValidity-subDays <= 0 && config.PwdValidity != 0 && userInfo.PwdUpdatedTime.String()[:10] != "0001-01-01" { + SendJsonResponse(c, resp.FAIL.ErrorDetail(errors.New("密码已失效,请联系管理员修改密码!")), nil) + return + } + // 密码强弱提醒 + if config.ForceUpdateState == 1 && config.MinPwdLevel > userInfo.PwdLevel { + SendJsonResponse(c, resp.FAIL.ErrorDetail(errors.New("密码强度弱,请联系管理员修改密码!")), nil) + return + } + + lastLogin, msg, uuidStr, err := loginInf.LoginV2(userInfo) + if err != nil { + SendJsonResponse(c, err, "") + return + } + c.SetCookie(conf.CookieName, uuidStr, 1*60*60*24, "/", "", false, false) + c.SetCookie(conf.CookieNameLastLogin, lastLogin, 1*60*60*24, "/", "", false, false) + + SendJsonResponse(c, resp.OK, msg) + } +} func GetUserInfo(c *gin.Context) { + token, _ := c.Cookie(conf.CookieName) conf.Logger.Info("当前token信息为", zap.String("bgToken", token)) header := c.GetHeader(conf.CookieName) diff --git a/src/router/logmanagementrouter.go b/src/router/logmanagementrouter.go index 4aa680e..fea98aa 100644 --- a/src/router/logmanagementrouter.go +++ b/src/router/logmanagementrouter.go @@ -31,12 +31,13 @@ func InitLogManagementRouter(e *gin.Engine) { } //userAccountAudit := logger.Group("/userAccountAudit", header.SetContext) - //{ - // //用户账户审计: - // userAccountAudit.GET("list", controller.LogUserAccountAuditList, header.AddLogMiddleware("用户账户审计", "/list", constant.OpTypeIntMap[constant.Find])) //用户账户审计列表 - // userAccountAudit.GET("list/export", controller.LogUserAccountAuditExport, header.AddLogMiddleware("用户账户审计", "/export", constant.OpTypeIntMap[constant.Export])) //用户账户审计列表导出 - // - //} + userAccountAudit := logger.Group("/userAccountAudit") + { + //用户账户审计: + userAccountAudit.GET("list", controller.LogUserAccountAuditList, header.AddLogMiddleware("用户账户审计", "/list", constant.OpTypeIntMap[constant.Find])) //用户账户审计列表 + userAccountAudit.GET("list/export", controller.LogUserAccountAuditExport, header.AddLogMiddleware("用户账户审计", "/export", constant.OpTypeIntMap[constant.Export])) //用户账户审计列表导出 + + } } } diff --git a/src/service/access_rule.go b/src/service/access_rule.go new file mode 100644 index 0000000..f8438af --- /dev/null +++ b/src/service/access_rule.go @@ -0,0 +1,139 @@ +package service + +import ( + "fmt" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" + "net" + "strings" + "time" + + "github.com/go-redis/redis" + "github.com/spf13/cast" + + "go.uber.org/zap" +) + +type AccessRuleSvc struct { +} + +// 校验用户访问的ip是否合法 +func (r *AccessRuleSvc) CheckIp(ip string, userId int) error { + rcon, err := client.GetRedisClient() + if err != nil { + conf.Logger.Error("redis err", zap.Error(err)) + return resp.RedisConnectError.ErrorDetail(err) + } + mode := 0 + modeVal, err := rcon.Get(conf.Options.AccessRuleModeKey) + if err != nil { + if err == redis.Nil { + // 查询数据库 + accessRuleMode, errCode := r.GetAccessRuleMode() + if errCode != nil { + conf.Logger.Error("db err", zap.Error(err)) + return resp.DbSelectError.ErrorDetail(err) + } + mode = accessRuleMode + err = rcon.Set(conf.Options.AccessRuleModeKey, accessRuleMode, 60*time.Second) + if err != nil { + return err + } + } else { + conf.Logger.Error("redis err", zap.Error(err)) + return resp.RedisExecError.ErrorDetail(err) + } + } else { + mode = cast.ToInt(modeVal) + } + switch mode { + case 0: // 访问规则模式关闭 + return nil + case 1: // 黑名单模式 + accessRules, errCode := r.GetAllAccessRules(1) + if errCode != nil { + conf.Logger.Error("db err", zap.Error(err)) + return resp.DbSelectError.ErrorDetail(err) + } + //查询登录用户是否加入访问规则 + ruleUserInfo, err := r.GetSystemRuleUser(userId) + if err != nil { + return resp.DbSelectError.ErrorDetail(err) + } + for i := range accessRules { + // 判断ip 是否合法 + fmt.Println(accessRules[i].RuleDetail) + ruleArr := strings.Split(accessRules[i].RuleDetail, "\n") + for _, rule := range ruleArr { + //IP校验+用户是否加入访问规则 + if r.checkIp(ip, strings.Trim(rule, " ")) == false && len(ruleUserInfo) > 0 { + conf.Logger.Error("访问规则error", zap.Error(err)) + return resp.FAIL.ErrorDetail(fmt.Errorf("您的IP:%s不在访问白名单内,禁止访问,请联系管理员。", ip)) + } + } + } + } + return nil +} + +func (r *AccessRuleSvc) checkIp(targetIp, ipRule string) bool { + parseIP := net.ParseIP(ipRule) + if parseIP != nil { + return parseIP.String() == targetIp + } else { + _, ipNet, _ := net.ParseCIDR(ipRule) + if ipNet != nil { + return ipNet.Contains(net.ParseIP(targetIp)) + } + } + return false +} + +// 获取访问规则模式 +func (r *AccessRuleSvc) GetAccessRuleMode() (int, error) { + db, err := client.GetDbClient() + if err != nil { + return 0, resp.DbConnectError.ErrorDetail(err) + } + var accessRuleMode int + _, err = db.Table("system_preference_config").Select("access_rule_state").Get(&accessRuleMode) + if err != nil { + return 0, resp.DbSelectError.ErrorDetail(err) + } + return accessRuleMode, nil +} + +// 查询所有的访问规则 +func (r *AccessRuleSvc) GetAllAccessRules(state int) ([]entity.SystemAccessRule, error) { + db, err := client.GetDbClient() + if err != nil { + return nil, resp.DbConnectError.ErrorDetail(err) + } + var ls []entity.SystemAccessRule + session := db.Table("system_access_rule").Where("rule_type = 1") + if state != 0 { + session.Where("state = ?", state) + } + if err := session.Find(&ls); err != nil { + return nil, resp.DbSelectError.ErrorDetail(err) + } + return ls, nil +} + +// 查询用户维护访问规则 +func (r *AccessRuleSvc) GetSystemRuleUser(userId int) ([]entity.SystemRuleUser, error) { + db, err := client.GetDbClient() + if err != nil { + return nil, resp.DbConnectError.ErrorDetail(err) + } + var ls []entity.SystemRuleUser + modelObj := db.Table("system_rule_user").Alias("sru") + modelObj.Join("INNER", []string{"system_access_rule", "sar"}, "sar.rule_id = sru.rule_id") + modelObj.Where("sru.user_id = ?", userId).And("sar.state = 1") + if err := modelObj.Find(&ls); err != nil { + return nil, resp.DbSelectError.ErrorDetail(err) + } + return ls, nil +} diff --git a/src/service/log_management.go b/src/service/log_management.go index 467f08c..8b6013a 100644 --- a/src/service/log_management.go +++ b/src/service/log_management.go @@ -232,228 +232,131 @@ func (s *LogManagement) LogUserBehaviorDelete(ids []string) (err error) { return nil } -//// LogUserAccountAuditList 行为日志列表 -//func (s *LogManagement) LogUserAccountAuditList(params *request.LogManagementListReq) ([]response.LogManagementRep, int64, error) { -// var logManagementRep = []response.LogManagementRep{} -// db, err := client.GetDbClient() -// if err != nil { -// err = res.DbConnectError.ErrorDetail(err) -// return nil, 0, err -// } -// -// modelObj := db.Table("system_user as u") -// modelObj.Join("left", "system_organization as org", "u.organization_id = org.organization_id"). -// Select(`u.ID, -// system_account, -// contact_phone, -// case is_admin when 1 then '业务系统账号' when 2 then '组织管理员账号' when 3 then '平台用户账号' when 4 then '超级管理员' end as is_admin, -// NAME, -// u.organization_id, -// last_access_time, -// case pwd_level when 1 then '弱' when 2 then '中' when 3 then '强' end as pwd_level, -// pwd_updated_time, -// date_part('day', now()-pwd_updated_time)as pwd_is_used , -// u.created_time `) -// -// // 条件查询 -// if params.Search != "" { -// keyword := util.SpecialEscape(params.Search) -// modelObj.Where("system_account ilike ? or contact_phone ilike ? or NAME ilike ?", "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%") -// } -// // 用户类型 -// if params.IsAdmin != 0 { -// modelObj.Where("is_admin = ?", params.IsAdmin) -// } -// // 上次访问时间 -// if params.StartAt != "" && params.EndAt != "" { -// applyEndTime, timeErr := time.Parse(jsonTime.LocalDateFormat, params.EndAt) -// if timeErr != nil { -// conf.Logger.Error("时间转换错误", zap.Error(timeErr)) -// return nil, 0, res.DataFailError.ErrorDetail(timeErr) -// } -// EndAt := applyEndTime.AddDate(0, 0, 1).Format(jsonTime.LocalDateFormat) -// modelObj.Where("last_access_time >= ? and last_access_time < ?", params.StartAt, EndAt) -// } -// // 活跃度 1: 低 2:中 3: 高 -// if params.Active != 0 { -// switch params.Active { -// case 1: -// modelObj.Where("last_access_time <= now()-interval '1 month'") -// case 2: -// modelObj.Where("last_access_time < now()-interval '1 week'").And("last_access_time > now()-interval '1 month'") -// case 3: -// modelObj.Where("last_access_time >= now()-interval '1 week'") -// } -// } -// // 密码强度 -// if params.PwdLevel != 0 { -// modelObj.Where("pwd_level = ? ", params.PwdLevel) -// } -// count, err := modelObj.OrderBy("last_access_time desc").Limit(int(params.Limit), int(params.Offset)).FindAndCount(&logManagementRep) -// if err != nil { -// conf.Logger.Error("查询用户账户审计列表失败", zap.Error(err)) -// err = res.DbSelectError.ErrorDetail(errors.New("查询用户账户审计列表失败")) -// return nil, 0, err -// } -// for k := range logManagementRep { -// // lastAccessTime := cast.ToTime(logManagementRep[k].LastAccessTimes) -// active := getActivity(logManagementRep[k].LastAccessTimes) -// logManagementRep[k].Active = active -// logManagementRep[k].LastAccessTime = jsonTime.JsonTime(logManagementRep[k].LastAccessTimes) -// } -// -// return logManagementRep, count, nil -//} -// -//func getActivity(date time.Time) string { -// // dates := cast.ToTime(date) -// var ( -// lastAccess = date.Unix() -// pastWeek = time.Now().AddDate(0, 0, -7).Unix() -// pastMonth = time.Now().AddDate(0, -1, 0).Unix() -// ) -// if lastAccess >= pastWeek { -// return "高" -// } -// if lastAccess < pastWeek && lastAccess > pastMonth { -// return "中" -// } -// if lastAccess <= pastMonth { -// return "低" -// } -// return "不活跃" -//} -// -//// LogUserAccountAuditExport 行为日志导出 -//func (s *LogManagement) LogUserAccountAuditExport(params *request.LogManagementListReq) (xlFile *xlsx.File, fileName string, err error) { -// xlFile = xlsx.NewFile() -// sheet, err := xlFile.AddSheet("sheet") -// if err != nil { -// conf.Logger.Error("xlFile.AddSheet err", zap.Error(err)) -// return -// } -// titleRow := sheet.AddRow() -// tmp := []string{ -// "序号", "账号", "手机号", "用户类型", "所属组织", "上次访问时间", "活跃度", "密码强度", "密码使用时长/天", "创建时间", -// } -// xlsRow2 := tools.NewRow(titleRow, tmp) -// err = xlsRow2.SetRowTitle() -// if err != nil { -// return -// } -// params.Page = 0 -// params.Limit = 20000 -// list, total, err := s.LogUserAccountAuditList(params) -// if total == 0 { -// err = errors.New("没有可导出的数据") -// return -// } else { -// fileName = "用户账号审计" + ".xlsx" -// } -// id := 0 -// for _, detail := range list { -// id += 1 -// currentRow := sheet.AddRow() -// var tmp = []string{ -// strconv.Itoa(id), detail.SystemAccount, detail.ContactPhone, detail.IsAdmin, detail.Name, cast.ToString(detail.LastAccessTime), -// detail.Active, detail.PwdLevel, strconv.Itoa(detail.PwdIsUsed), cast.ToString(detail.CreatedTime), -// } -// newRow := tools.NewRow(currentRow, tmp) -// err := newRow.GenerateRow() -// if err != nil { -// return nil, "", err -// } -// } -// return -//} -// -//// 用户行为审计列表 -//func (s *LogManagement) LogUserBehaviorListEs(params *request.LogUserBehaviorListReq) (list []response.SystemUserBehaviorList, total int64, err error) { -// es, err := client.GetEsConnection(conf.EsDoc.Index) -// if err != nil { -// return nil, 0, res.DbConnectError.ErrorDetail(err) -// } -// // boolQuery 可用于组合查询 -// boolQuery := elastic.NewBoolQuery() -// // 条件查询 -// if params.Search != "" { -// keyword := util.SpecialEscape(params.Search) -// boolQuery.Filter( -// elastic.NewBoolQuery().Should( -// elastic.NewWildcardQuery("system_account.keyword", fmt.Sprintf("*%s*", keyword)), -// elastic.NewWildcardQuery("system_module.keyword", fmt.Sprintf("*%s*", keyword)), -// elastic.NewWildcardQuery("operate_addr.keyword", fmt.Sprintf("*%s*", keyword)), -// )) -// } -// // 用户类型 -// if params.IsAdmin != 0 { -// boolQuery.Filter(elastic.NewMatchQuery("is_admin", params.IsAdmin)) -// } -// // 上次访问时间 -// if params.StartAt != "" && params.EndAt != "" { -// applyEndTime, timeErr := time.Parse(jsonTime.LocalDateFormatEs, params.EndAt) -// if timeErr != nil { -// conf.Logger.Error("时间转换错误", zap.Error(timeErr)) -// return nil, 0, res.DataFailError.ErrorDetail(timeErr) -// } -// EndAt := applyEndTime.AddDate(0, 0, 1).Format(jsonTime.LocalDateFormatEs) -// boolQuery.Filter(elastic.NewRangeQuery("created_time").Gte(params.StartAt)) -// boolQuery.Filter(elastic.NewRangeQuery("created_time").Lt(EndAt)) -// } -// // 操作类型 -// if params.OperateType != "" { -// boolQuery.Filter(elastic.NewMatchQuery("operate_type", params.OperateType)) -// } -// // 请求方式 -// if params.ReqMethod != 0 { -// boolQuery.Filter(elastic.NewMatchQuery("req_method", params.ReqMethod)) -// } -// // 操作状态 -// if params.OperateStatus != 0 { -// boolQuery.Filter(elastic.NewMatchQuery("operate_status", params.OperateStatus)) -// } -// searchResult, err := es.Conn.Search(). -// Index(conf.EsDoc.Index). -// Query(boolQuery). -// SortWithInfo(elastic.SortInfo{Field: "created_time_unix", Ascending: false, UnmappedType: "keyword"}). -// From(int((params.Page - 1) * params.Limit)).Size(cast.ToInt(params.Limit)). -// Pretty(true). -// Do(context.Background()) -// if err != nil { -// conf.Logger.Error("查询用户行为审计列表失败", zap.Error(err)) -// return nil, 0, res.DbSelectError.ErrorDetail(err) -// } -// bytes, err := json.Marshal(searchResult) -// if err != nil { -// conf.Logger.Error("json.Marshal序列化用户行为信息失败", zap.Error(err)) -// return nil, 0, res.JsonMarshalError.ErrorDetail(err) -// } -// resultMap := map[string]interface{}{} -// err = json.Unmarshal(bytes, &resultMap) -// if err != nil { -// conf.Logger.Error("序列化用户行为信息失败", zap.Error(err)) -// return nil, 0, res.JsonMarshalError.ErrorDetail(err) -// } -// data, has := resultMap["hits"] -// esData := response.BehaviorEsListInfo{} -// if has { -// bytes, err := json.Marshal(data) -// if err != nil { -// conf.Logger.Error("序列化用户行为信息失败", zap.Error(err)) -// return nil, 0, err -// } -// err = json.Unmarshal(bytes, &esData) -// if err != nil { -// conf.Logger.Error("序列化用户行为信息失败", zap.Error(err)) -// return nil, 0, err -// } -// for k, v := range esData.Hits { -// esData.Hits[k].Source.Id = v.Id -// esData.Hits[k].Source.Xh = (params.Page) * int64(k+1) -// list = append(list, esData.Hits[k].Source) -// } -// } -// total = cast.ToInt64(esData.Total["value"]) -// return list, total, nil -//} -// +// LogUserAccountAuditList 行为日志列表 +func (s *LogManagement) LogUserAccountAuditList(params *request.LogManagementListReq) ([]response.LogManagementRep, int64, error) { + var logManagementRep []response.LogManagementRep + db, err := client.GetDbClient() + if err != nil { + err = resp.DbConnectError.ErrorDetail(err) + return nil, 0, err + } + + modelObj := db.Table("system_user as u") + modelObj.Join("left", "system_organization as org", "u.organization_id = org.organization_id"). + Select(`u.id, + u.system_account, + u.phone, + u.name, + u.organization_id, + u.last_access_time, + case u.pwd_level when 1 then '弱' when 2 then '中' when 3 then '强' end as pwd_level, + u.pwd_updated_time, + date_part('day', now()-u.pwd_updated_time)as pwd_is_used , + u.created_time `) + + // 条件查询 + if params.Search != "" { + keyword := util.SpecialEscape(params.Search) + modelObj.Where("system_account like ? or phone like ? or NAME like ?", "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%") + } + // 上次访问时间 + if params.StartAt != "" && params.EndAt != "" { + applyEndTime, timeErr := time.Parse(jsontime.LocalDateFormat, params.EndAt) + if timeErr != nil { + conf.Logger.Error("时间转换错误", zap.Error(timeErr)) + return nil, 0, resp.DbSelectError.ErrorDetail(timeErr) + } + EndAt := applyEndTime.AddDate(0, 0, 1).Format(jsontime.LocalDateFormat) + modelObj.Where("last_access_time >= ? and last_access_time < ?", params.StartAt, EndAt) + } + // 活跃度 1: 低 2:中 3: 高 + if params.Active != 0 { + switch params.Active { + case 1: + modelObj.Where("last_access_time <= now()-interval '1 month'") + case 2: + modelObj.Where("last_access_time < now()-interval '1 week'").And("last_access_time > now()-interval '1 month'") + case 3: + modelObj.Where("last_access_time >= now()-interval '1 week'") + } + } + // 密码强度 + if params.PwdLevel != 0 { + modelObj.Where("pwd_level = ? ", params.PwdLevel) + } + count, err := modelObj.OrderBy("last_access_time desc").Limit(params.GetPageSize(), params.GetOffset()).FindAndCount(&logManagementRep) + if err != nil { + conf.Logger.Error("查询用户账户审计列表失败", zap.Error(err)) + err = resp.DbSelectError.ErrorDetail(errors.New("查询用户账户审计列表失败")) + return nil, 0, err + } + for k := range logManagementRep { + active := getActivity(time.Time(logManagementRep[k].LastAccessTimes)) + logManagementRep[k].Active = active + logManagementRep[k].LastAccessTime = logManagementRep[k].LastAccessTimes + } + + return logManagementRep, count, nil +} + +func getActivity(date time.Time) string { + // dates := cast.ToTime(date) + var ( + lastAccess = date.Unix() + pastWeek = time.Now().AddDate(0, 0, -7).Unix() + pastMonth = time.Now().AddDate(0, -1, 0).Unix() + ) + if lastAccess >= pastWeek { + return "高" + } + if lastAccess < pastWeek && lastAccess > pastMonth { + return "中" + } + if lastAccess <= pastMonth { + return "低" + } + return "不活跃" +} + +// LogUserAccountAuditExport 行为日志导出 +func (s *LogManagement) LogUserAccountAuditExport(params *request.LogManagementListReq) (xlFile *xlsx.File, fileName string, err error) { + xlFile = xlsx.NewFile() + sheet, err := xlFile.AddSheet("sheet") + if err != nil { + conf.Logger.Error("xlFile.AddSheet err", zap.Error(err)) + return + } + titleRow := sheet.AddRow() + tmp := []string{ + "序号", "账号", "手机号", "所属组织", "上次访问时间", "活跃度", "密码强度", "密码使用时长/天", "创建时间", + } + xlsRow2 := tools.NewRow(titleRow, tmp) + err = xlsRow2.SetRowTitle() + if err != nil { + return + } + //params.Page = 0 + //params.PageSize = 20000 + list, total, err := s.LogUserAccountAuditList(params) + if total == 0 { + err = errors.New("没有可导出的数据") + return + } else { + fileName = "用户账号审计" + ".xlsx" + } + id := 0 + for _, detail := range list { + id += 1 + currentRow := sheet.AddRow() + var tmp = []string{ + strconv.Itoa(id), detail.SystemAccount, detail.Phone, detail.Name, detail.LastAccessTime.String(), + detail.Active, detail.PwdLevel, strconv.Itoa(detail.PwdIsUsed), detail.CreatedTime.String(), + } + newRow := tools.NewRow(currentRow, tmp) + err := newRow.GenerateRow() + if err != nil { + return nil, "", err + } + } + return +} diff --git a/src/service/login.go b/src/service/login.go index 9770fa9..9f129be 100644 --- a/src/service/login.go +++ b/src/service/login.go @@ -11,6 +11,7 @@ import ( "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/tools" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" + "strconv" "image/color" "strings" @@ -30,6 +31,36 @@ type UserSvc struct { PassWord string } +// 获取用户信息(登录前) +func (u *UserSvc) GetUserInfo(phone string) (userInfo entity.SystemUserInfo, err error) { + db, err := client.GetDbClient() + if err != nil { + return userInfo, resp.DbConnectError.ErrorDetail(err) + } + var userRole []entity.SystemUserInfo + // 查询用户 + userModel := db.Table("system_user") + userModel.Join("LEFT", []string{"system_user_role", "sur"}, "sur.user_id = system_user.id") + userModel.Join("LEFT", []string{"system_role", "sr"}, "sur.role_id = sr.role_id") + userModel.Join("LEFT", []string{"system_organization", "so"}, "so.organization_id = system_user.organization_id") + userModel.Select("system_user.*,so.name as org_name") + if phone == "" { + userModel.Where("system_account = ?", u.SystemAccount) + } else { + userModel.Where("system_user.phone = ?", phone) + } + err = userModel.Where("system_user.state = 1").Find(&userRole) + if err != nil { + return userInfo, resp.DbSelectError.ErrorDetail(err) + } + if len(userRole) == 0 && phone == "" { + return userInfo, resp.FAIL.WithError(errors.New("该账户未启用,不能登录")) + } else if len(userRole) == 0 && phone != "" { + return userInfo, resp.FAIL.WithError(errors.New("手机号未注册!")) + } + return userInfo, nil +} + func (u *UserSvc) Login() (msg string, uuidStr string, last_login string, err error) { db, err := client.GetDbClient() if err != nil { @@ -241,13 +272,13 @@ func (u *UserSvc) VerifyCaptcha(id, value string) (err error) { } // 用户锁定(此逻辑包含锁定规则以及锁定key) -func (u *UserSvc) UserLock(systemId, pwd string, config *entity.SystemPreferenceConfig) (err error) { +func (u *UserSvc) UserLock(userId int, pwd string, config *entity.SystemPreferenceConfig) (err error) { r, err := client.GetRedisClient() if err != nil { return resp.RedisConnectError.ErrorDetail(err) } - startLockKey := fmt.Sprintf("%s-%s", client.RULEKEY, systemId) - lockSystemKey := fmt.Sprintf("%s-%s", client.LOCKKEY, systemId) + startLockKey := fmt.Sprintf("%s-%v", client.RULEKEY, userId) + lockSystemKey := fmt.Sprintf("%s-%v", client.LOCKKEY, userId) errorNum, err := r.Get(startLockKey) if err != nil && err != redis.Nil { conf.Logger.Error("获取密码插入次数失败", zap.Error(err)) @@ -280,27 +311,28 @@ func (u *UserSvc) UserLock(systemId, pwd string, config *entity.SystemPreference } } // 判断是否输错密码 - h := md5.New() - _, err = h.Write([]byte(strings.ToUpper(systemId + "-" + u.PassWord))) + + incr, err := r.Incr(startLockKey) if err != nil { - conf.Logger.Error("加密错误", zap.Error(err)) + conf.Logger.Error("写入错误次数失败", zap.Error(err)) return err } - upperMd5Pass := strings.ToUpper(hex.EncodeToString(h.Sum(nil))) - if upperMd5Pass != pwd { - incr, err := r.Incr(startLockKey) + conf.Logger.Info("当前错误次数为", zap.Int64("incr", incr)) + if int(incr) >= config.LoginPwdError { + err = r.Set(lockSystemKey, 0, time.Duration(config.LoginLockTime)*time.Minute) if err != nil { - conf.Logger.Error("写入错误次数失败", zap.Error(err)) return err } - conf.Logger.Info("当前错误次数为", zap.Int64("incr", incr)) - if int(incr) >= config.LoginPwdError { - err = r.Set(lockSystemKey, 0, time.Duration(config.LoginLockTime)*time.Minute) - if err != nil { - return err - } - } - return fmt.Errorf("请正确输入密码,连续%d次输错密码,账号将被锁定,还可重试%d次", incr, config.LoginPwdError-cast.ToInt(incr)) + } + return fmt.Errorf("请正确输入密码,连续%d次输错密码,账号将被锁定,还可重试%d次", incr, config.LoginPwdError-cast.ToInt(incr)) + +} + +func (u *UserSvc) UserUnLock(userId int) error { + startLockKey := fmt.Sprintf("%s-%v", client.RULEKEY, userId) + r, err := client.GetRedisClient() + if err != nil { + return resp.RedisConnectError.ErrorDetail(err) } // 密码正确 删除 锁定文件 if err := r.Del(startLockKey); err != nil { @@ -309,3 +341,81 @@ func (u *UserSvc) UserLock(systemId, pwd string, config *entity.SystemPreference } return nil } + +// 登录 +func (u *UserSvc) LoginV2(userInfo entity.SystemUserInfo) (last_login_time, msg string, uuidStr string, err error) { + redisCli, err := client.GetRedisClient() + if err != nil { + return "", "", "", resp.RedisConnectError.ErrorDetail(err) + } + // TODO 用户数据、登录时间存入redis + last_login_time, uuidStr, err = u.SaveUserInfo(userInfo, redisCli) + if err != nil { + conf.Logger.Error("保存用户数据失败", zap.Error(err)) + return "", "", "", resp.RedisExecError.ErrorDetail(err) + } + msg = "登录成功" + conf.Logger.Info("登录成功", zap.String("msg", msg)) + //go func() { + // // 最后心跳时间 + // finalHeartBeatMap := map[string]interface{}{ + // "final_heartbeat_unix": time.Now().UnixNano() / 1e6, + // "final_heartbeat_flag": false, + // } + // finalHeartBeatByte, _ := json.Marshal(finalHeartBeatMap) + // redisCli.HSet(conf.FinalHeartBeatUnixKey, userInfo.Id, string(finalHeartBeatByte)) + //}() + return +} + +// 保存用户信息 +func (u *UserSvc) SaveUserInfo(userInfo entity.SystemUserInfo, r client.Redis) (last_login, uuidStr string, err error) { + uu := uuid.NewV4() + uuidStr = uu.String() + // 存入redis + b, err := json.Marshal(userInfo) + op := SystemOptionsSvc{} + config, err := op.GetSystemOptions() + expireTime := time.Duration(config.SessionValidity) + if err != nil { + return "", "", err + } + err = r.Set(uuidStr, string(b), time.Minute*expireTime) + if err != nil { + conf.Logger.Error("保存用户数据失败", zap.Error(err)) + return "", "", err + } + + // 登录时间计录在map + loginMap := make(map[string]string, 0) + cont, _ := r.Get("LOGIN-TIME") + if cont != "" { + err = json.Unmarshal([]byte(cont), &loginMap) + if err != nil { + return "", "", err + } + } + now := time.Now().Format(conf.LocalDateTimeFormat) + if _, ok := loginMap[u.SystemAccount]; ok { + last_login = loginMap[u.SystemAccount] + } else { + last_login = now + } + loginMap[u.SystemAccount] = now + a, err := json.Marshal(loginMap) + if err != nil { + return "", "", err + } + err = r.Set("LOGIN-TIME", string(a), -1) + if err != nil { + conf.Logger.Error("登录失败", zap.Error(err)) + return "", "", err + } + // 保存用户id以及token信息 + err = r.LPush(strconv.Itoa(userInfo.Id), uuidStr) + if err != nil { + conf.Logger.Error("保存用户信息失败", zap.Error(err)) + return "", "", err + } + return last_login, uuidStr, nil +} diff --git a/src/service/system_user.go b/src/service/system_user.go index cbc9f8a..37b38e4 100644 --- a/src/service/system_user.go +++ b/src/service/system_user.go @@ -140,8 +140,16 @@ func (o *User) OrgAddUser(input request.OrgUserInput) error { conf.Logger.Error("密码处理出错", zap.Error(err)) return nil, resp.DbInsertError.ErrorDetail(err) } - _, err = s.Table("system_user").Where("id = ?", userModel.Id).Update(map[string]string{ - "password": upperMd5Pass, + pwdLevel, err := util.GetPwdLevel(input.Password) + if err != nil { + conf.Logger.Error("密码处理出错", zap.Error(err)) + return nil, resp.DbInsertError.ErrorDetail(err) + } + _, err = s.Table("system_user").Where("id = ?", userModel.Id).Update(map[string]interface{}{ + "password": upperMd5Pass, + "pwd_level": pwdLevel, + "pwd_updated_time": jsontime.Time(time.Now()), + "pwd_force_status": 0, }) if err != nil { conf.Logger.Error("密码处理出错", zap.Error(err)) @@ -346,10 +354,18 @@ func SystemUserEditPassword(params request.SystemUserEditPasswordReq) (err error if err != nil { return } + pwdLevel, err := util.GetPwdLevel(params.Password) + if err != nil { + return + } SystemUser := entity.SystemUser{} SystemUser.Password = password + SystemUser.PwdLevel = pwdLevel + SystemUser.PwdForceStatus = 0 + SystemUser.PwdUpdatedTime = time.Now() - _, err = db.Where("id=?", oldSystemUser.Id).Where("is_deleted = 0").Update(&SystemUser) + _, err = db.Where("id=?", oldSystemUser.Id).Where("is_deleted = 0").Cols("password,pwd_level,pwd_force_status,pwd_updated_time"). + Update(&SystemUser) if err != nil { conf.Logger.Error("修改系统账户失败", zap.Error(err)) err = resp.DbUpdateError.ErrorDetail(errors.New("修改系统账户失败")) @@ -359,32 +375,40 @@ func SystemUserEditPassword(params request.SystemUserEditPasswordReq) (err error } // ResetSystemUserPassword 批量重置密码 -func ResetSystemUserPassword(params request.ResetSystemUserPasswordReq) (err error) { +func ResetSystemUserPassword(params request.ResetSystemUserPasswordReq) error { db, err := client.GetDbClient() if err != nil { err = resp.DbConnectError.ErrorDetail(err) - return + return err } str, EncryptErr := util.EncryptPwd(defaultPassword) if EncryptErr != nil { err = resp.DbUpdateError.ErrorDetail(err) + return err } for _, v := range params.Ids { password, SolvePwdErr := SolvePassword(v, str) if SolvePwdErr != nil { return SolvePwdErr } - updateData := entity.SystemUser{} - updateData.Password = password - _, err = db.Where("id=?", v).Update(&updateData) + pwdLevel, err := util.GetPwdLevel(password) + if err != nil { + return err + } + SystemUser := entity.SystemUser{} + SystemUser.Password = password + SystemUser.PwdLevel = pwdLevel + SystemUser.PwdForceStatus = 0 + SystemUser.PwdUpdatedTime = time.Now() + _, err = db.Where("id=?", v).Cols("password,pwd_level,pwd_force_status,pwd_updated_time").Update(&SystemUser) if err != nil { conf.Logger.Error("重置账户密码失败", zap.Error(err)) err = resp.DbUpdateError.ErrorDetail(errors.New("重置账户密码失败")) - return + return err } } - return + return nil } // SolvePassword 密码处理 diff --git a/src/util/common.go b/src/util/common.go index 8310d5b..cdea601 100644 --- a/src/util/common.go +++ b/src/util/common.go @@ -9,9 +9,13 @@ package util import ( + "errors" + "fmt" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "net/http" "regexp" "strings" + "time" "unsafe" "github.com/Luzifer/go-openssl/v4" @@ -41,6 +45,58 @@ func DecryptPwd(pwd string) (str string, err error) { return string(dec), err } +type StrReplaceStruct struct { + CapitalLetter int `json:"capital_letter"` + LowercaseLetters int `json:"lowercase_letters"` + Number int `json:"number"` + OtherString int `json:"other_string"` +} + +// 获取密码强度 +func GetPwdLevel(pwd string) (level int, err error) { + password, err := DecryptPwd(pwd) + if err != nil { + return 0, err + } + conf.Logger.Info(fmt.Sprintf("解析密码为: %s", password)) + var ( + groupCount int + arrayGroup []int + ) + arrayGroup = append(arrayGroup, StrReplaceAllString(password).CapitalLetter, StrReplaceAllString(password).LowercaseLetters, StrReplaceAllString(password).Number, StrReplaceAllString(password).OtherString) + for _, v := range arrayGroup { + if v > 0 { + groupCount += 1 + } + } + if groupCount == 0 { + return 0, errors.New("密码非法!") + } else if groupCount == 1 { + level = 1 + } else if groupCount == 2 { + level = 2 + } else if groupCount >= 3 { + level = 3 + } + return level, nil +} + +func StrReplaceAllString(s2 string) (strReplace StrReplaceStruct) { + for i := strReplace.OtherString; i < len(s2); i++ { + switch { + case 64 < s2[i] && s2[i] < 91: + strReplace.CapitalLetter += 1 + case 96 < s2[i] && s2[i] < 123: + strReplace.LowercaseLetters += 1 + case 47 < s2[i] && s2[i] < 58: + strReplace.Number += 1 + default: + strReplace.OtherString += 1 + } + } + return strReplace +} + func EncryptPwd(password string) (pwd string, err error) { passphrase := "swuE9cmCZQwrkYRV" ecs, err := OpenSslManager.EncryptBytes(passphrase, []byte(password), openssl.BytesToKeyMD5) @@ -72,3 +128,26 @@ func SpecialEscape(keyword string) string { func Bytes2Str(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } + +// 计算日期差(t1>t2) +func SubDays(t1, t2 time.Time) (day int) { + swap := false + if t1.Unix() < t2.Unix() { + t_ := t1 + t1 = t2 + t2 = t_ + swap = true + } + day = int(t1.Sub(t2).Hours() / 24) + // 计算在被24整除外的时间是否存在跨自然日 + if int(t1.Sub(t2).Milliseconds())%86400000 > int(86400000-t2.Unix()%86400000) { + day += 1 + } + if swap { + day = -day + } + if day < 0 { + day = 0 + } + return +} -- 2.26.0