package service import ( "crypto/md5" "encoding/hex" "errors" "fmt" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" "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/common/tools" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" "strconv" "image/color" "strings" "time" "github.com/go-redis/redis" "github.com/mojocn/base64Captcha" uuid "github.com/satori/go.uuid" "github.com/spf13/cast" "go.uber.org/zap" "k8s.io/apimachinery/pkg/util/json" ) type UserSvc struct { SystemAccount string 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 { return "", "", "", resp.DbConnectError.WithError(err) } // 查询用户 var userInfo entity.SystemUserInfo _, err = db.Table("system_user"). Select("system_user.*"). Where("system_account = ?", u.SystemAccount).Get(&userInfo) if err != nil { return } if userInfo.Id == 0 { conf.Logger.Error("用户名或密码错误", zap.Error(err)) return "", "", "", resp.FAIL.WithMsg("用户名或密码错误") } if userInfo.State == 0 { conf.Logger.Error("账号未启用", zap.Error(err)) return "", "", "", resp.FAIL.WithMsg("账号未启用") } redisCli, err := client.GetRedisClient() if err != nil { return "", "", "", resp.RedisConnectError.WithError(err) } lockKey := fmt.Sprintf("so-operation-user-lock-%v", userInfo.Id) haslock, err := redisCli.Get(lockKey) if err != nil && err != redis.Nil { conf.Logger.Error("获取密码插入次数失败", zap.Error(err)) return "", "", "", resp.DbSelectError.WithError(err) } else if haslock == "" { if err := redisCli.Set(lockKey, 0, conf.LockDuration); err != nil { conf.Logger.Error("密码插入次数插入redis失败", zap.Error(err)) return "", "", "", resp.DbInsertError.WithError(err) } } else if cast.ToInt(haslock) >= conf.LockErrorNumber { if ttl, err := redisCli.Ttl(lockKey); err != nil { msg = "错误次数达到上限,请稍后重试" } else { if ttl.Seconds() <= 0 { if err := redisCli.Del(lockKey); err != nil { msg = "删除错误次数错误,请稍后重试" } ttl = time.Second } ttl := int(ttl.Seconds()) if ttl >= 3600 { msg = fmt.Sprintf("错误次数达到上限,请%d小时后重试", ttl/3600) } else if ttl >= 60 && ttl < 3600 { msg = fmt.Sprintf("错误次数达到上限,请%d分钟后重试", ttl/60) } else { msg = fmt.Sprintf("错误次数达到上限,请%d秒后重试", ttl) } } err = errors.New(msg) if err != nil { conf.Logger.Error(msg, zap.Error(err)) return "", "", "", resp.RedisExecError.WithError(err) } } h := md5.New() _, err = h.Write([]byte(strings.ToUpper(fmt.Sprintf("%d-%s", userInfo.Id, u.PassWord)))) if err != nil { conf.Logger.Error("加密错误", zap.Error(err)) return "", "", "", resp.FAIL.WithError(err) } uppperMd5Pass := strings.ToUpper(hex.EncodeToString(h.Sum(nil))) if uppperMd5Pass != userInfo.Password { incr, err := redisCli.Incr(lockKey) if err != nil { conf.Logger.Error("写入错误次数失败", zap.Error(err)) return "", "", "", resp.RedisExecError.WithError(err) } conf.Logger.Info("当前错误次数为", zap.Int64("incr", incr)) //再次错误执行续期 expire, err := redisCli.Expire(lockKey, conf.LockDuration) if err != nil { conf.Logger.Error("错误次数续期错误", zap.Error(err)) return "", "", "", resp.RedisExecError.WithError(err) } conf.Logger.Info("续期结果为", zap.Bool("expire", expire)) return "", "", "", resp.FAIL.WithMsg("用户名或密码错误") } //密码正确 删除 锁定文件 if err := redisCli.Del(lockKey); err != nil { conf.Logger.Error("删除锁定文件错误", zap.Error(err)) return "", "", "", resp.RedisExecError.WithError(err) } uu := uuid.NewV4() uuidStr = uu.String() // 存入redis b, err := json.Marshal(userInfo) if err != nil { return "", "", "", resp.FAIL.WithError(err) } err = redisCli.Set(uuidStr, string(b), time.Minute*60*24) if err != nil { conf.Logger.Error("登录失败", zap.Error(err)) return "", "", "", resp.FAIL.WithError(err) } msg = "登录成功" conf.Logger.Info("登录成功", zap.String("msg", msg)) //登录时间计录在map loginMap := make(map[string]string, 0) cont, _ := redisCli.Get("LOGIN-TIME") //if err != nil { // conf.Logger.Error("获取登录时间失败", zap.Error(err)) // return "", "", "", res.DataFailError.ErrorDetail(err) //} if cont != "" { err = json.Unmarshal([]byte(cont), &loginMap) if err != nil { return "", "", "", resp.FAIL.WithError(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 "", "", "", resp.FAIL.WithError(err) } err = redisCli.Set("LOGIN-TIME", string(a), -1) if err != nil { conf.Logger.Error("登录失败", zap.Error(err)) return "", "", "", resp.FAIL.WithError(err) } return } func (u *UserSvc) GetCurUser(token string) (m entity.SystemUserInfo, err error) { rcon, err := client.GetRedisClient() if err != nil { return m, resp.RedisConnectError.WithError(err) } result, err := rcon.Get(token) if err != nil { if err.Error() == "redis: nil" { err = errors.New("登录超时") return m, resp.FAIL.WithError(err) } else { conf.Logger.Error("获取token失败", zap.Error(err)) return m, resp.FAIL.WithError(err) } } if result == "" { err = errors.New("登录超时") return m, resp.FAIL.WithError(err) } err = json.Unmarshal([]byte(result), &m) if err != nil { return m, resp.FAIL.WithError(err) } m.Password = "" return } func (u *UserSvc) UserLogout(cookie string) (msg string, err error) { // 清理redis redisCli, err := client.GetRedisClient() if err != nil { return "", resp.FAIL.WithError(err) } err = redisCli.Del(cookie) if err != nil { msg = "登出失败" return msg, resp.FAIL.WithError(err) } return "登出成功", nil } func (u *UserSvc) GetCaptcha(width, height int) (captcha response.Captcha, err error) { param := tools.ConfigJsonBody{ CaptchaType: "string", DriverString: &base64Captcha.DriverString{ Height: height, Width: width, NoiseCount: 0, ShowLineOptions: 2, Length: 4, Source: "124567890", BgColor: &color.RGBA{255, 255, 255, 1}, //Fonts: []string{"3Dumb.ttf", "ApothecaryFont.ttf", "Comismsh.ttf", "DENNEthree-dee.ttf", "DeborahFancyDress.ttf", // "Flim-Flam.ttf", "RitaSmith.ttf", "actionj.ttf", "chromohv.ttf"}, Fonts: []string{"DENNEthree-dee.ttf"}, }, } captcha.Id, captcha.Captcha, err = tools.GenerateCaptcha(param) return } func (u *UserSvc) VerifyCaptcha(id, value string) (err error) { param := tools.ConfigJsonBody{} param.Id = id param.VerifyValue = strings.ToLower(value) right := tools.Verify(param) if !right { err = errors.New("验证码不正确") conf.Logger.Error("验证码不正确", zap.Error(err)) } return } // 用户锁定(此逻辑包含锁定规则以及锁定key) 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-%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)) return resp.RedisExecError.ErrorDetail(err) } else if errorNum == "" { if err := r.Set(startLockKey, 0, time.Duration(config.LoginLimitTime)*time.Hour); err != nil { conf.Logger.Error("密码插入次数插入redis失败", zap.Error(err)) return resp.RedisExecError.ErrorDetail(err) } } else if cast.ToInt(errorNum) >= config.LoginPwdError { // 错误上限, 自动锁定 has, err := r.Exist(lockSystemKey) if err != nil { return err } if has == 1 { // 获取锁定规则过期时间 ttl, err := r.Ttl(startLockKey) if err != nil { conf.Logger.Error("获取锁定规则过期时间失败", zap.Error(err)) return resp.RedisExecError.ErrorDetail(err) } return resp.FAIL.ErrorDetail(fmt.Errorf("您已经%s次输错密码,账号已锁定,请在%s分后重试!", errorNum, ttl/60)) } // 如果不存在过期时间key,则代表锁定时间已过期,删除锁定规则,重新进行新的锁定规则 err = r.Del(startLockKey) if err != nil { conf.Logger.Error("删除锁定规则文件失败", zap.Error(err)) return resp.RedisExecError.ErrorDetail(err) } } // 判断是否输错密码 incr, err := r.Incr(startLockKey) 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)) } 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 { conf.Logger.Error("删除锁定文件错误", zap.Error(err)) return err } 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 }