login.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package account
  2. import (
  3. "encoding/base64"
  4. "encoding/hex"
  5. "errors"
  6. "fmt"
  7. "mtp20access/global"
  8. accountModel "mtp20access/model/account"
  9. "mtp20access/model/account/request"
  10. jwtRequest "mtp20access/model/common/request"
  11. "mtp20access/packet"
  12. "sync"
  13. "mtp20access/utils"
  14. "strconv"
  15. "go.uber.org/zap"
  16. )
  17. var (
  18. mtx sync.RWMutex
  19. curSessionID int = 90000 // 本服务SessionID从90000开始,以避免与旧登录服务重叠
  20. )
  21. // Login 用户登录
  22. func Login(req request.LoginReq, addr string) (loginaccount *accountModel.Loginaccount, token string, expiresAt int64, err error) {
  23. // 分别尝试用LoginID、LoginCode和手机号码进行登录
  24. loginaccount, err = getLoginAccount(req.UserName, req.Password)
  25. if err != nil {
  26. return
  27. }
  28. // 判断用户状态
  29. if loginaccount.LOGINSTATUS == 2 {
  30. err = errors.New("账户已冻结")
  31. return
  32. }
  33. if loginaccount.LOGINSTATUS == 3 {
  34. err = errors.New("账户已注销")
  35. return
  36. }
  37. // 生成Token,并写入Redis
  38. if token, expiresAt, err = buildRedisLoginInfo(*loginaccount, addr, req.ClientType); err != nil {
  39. return
  40. }
  41. return
  42. }
  43. // getLoginAccount 分别尝试用LoginID、LoginCode和手机号码进行登录
  44. func getLoginAccount(userName string, password string) (loginaccount *accountModel.Loginaccount, err error) {
  45. // 密码解密(5.0报文解密)
  46. var d []byte
  47. if _, err = base64.StdEncoding.Decode(d, []byte(password)); err != nil {
  48. return
  49. }
  50. p, err := packet.Decrypt(d, packet.AESKey, true)
  51. if err != nil {
  52. return
  53. }
  54. pwd := string(p)
  55. // 通过LoginID查询
  56. if loginID, _ := strconv.Atoi(userName); loginID != 0 {
  57. loginaccount = &accountModel.Loginaccount{
  58. LOGINID: int64(loginID),
  59. PASSWORD: utils.EncoderSha256(fmt.Sprintf("%s%s", userName, pwd)), // 构建数据库存储的密码
  60. }
  61. if has, _ := global.M2A_DB.Get(loginaccount); has {
  62. return
  63. }
  64. }
  65. // 通过LoginCode查询
  66. loginaccount = &accountModel.Loginaccount{
  67. LOGINCODE: userName,
  68. }
  69. if has, _ := global.M2A_DB.Get(loginaccount); has {
  70. // 构建数据库存储的密码
  71. if loginaccount.PASSWORD == utils.EncoderSha256(fmt.Sprintf("%d%s", loginaccount.LOGINID, pwd)) {
  72. return
  73. }
  74. }
  75. // 通过手机号码查询,需要AES加密
  76. key, _ := hex.DecodeString(utils.AESSecretKey)
  77. if mobileEncrypted, _ := utils.AESEncrypt([]byte(userName), key); mobileEncrypted != nil {
  78. loginaccount = &accountModel.Loginaccount{
  79. MOBILE: string(mobileEncrypted),
  80. }
  81. if has, _ := global.M2A_DB.Get(loginaccount); has {
  82. // 构建数据库存储的密码
  83. if loginaccount.PASSWORD == utils.EncoderSha256(fmt.Sprintf("%d%s", loginaccount.LOGINID, pwd)) {
  84. return
  85. }
  86. }
  87. }
  88. err = errors.New("错误的用户名或密码")
  89. return
  90. }
  91. // newSessionID 获取
  92. func newSessionID() int {
  93. mtx.Lock()
  94. curSessionID += 1
  95. mtx.Unlock()
  96. return curSessionID
  97. }
  98. // buildRedisLoginInfo 生成Token,并写入Redis
  99. func buildRedisLoginInfo(loginaccount accountModel.Loginaccount, addr string, group int) (token string, expiresAt int64, err error) {
  100. // 生成SessionID
  101. sessionID := newSessionID()
  102. // 生成本服务Token
  103. j := &utils.JWT{SigningKey: []byte(global.M2A_CONFIG.JWT.SigningKey)} // 唯一签名
  104. claims := j.CreateClaims(jwtRequest.BaseClaims{
  105. LoginID: int(loginaccount.LOGINID),
  106. Group: group,
  107. SessionID: sessionID,
  108. })
  109. token, err = j.CreateToken(claims)
  110. if err != nil {
  111. global.M2A_LOG.Error("生成本服token失败", zap.Error(err))
  112. return
  113. }
  114. expiresAt = claims.RegisteredClaims.ExpiresAt.Unix()
  115. loginLogin := global.LoginRedis{
  116. LoginID: strconv.Itoa(int(loginaccount.LOGINID)),
  117. UserID: strconv.Itoa(int(loginaccount.USERID)),
  118. SessionID: strconv.Itoa(sessionID),
  119. Token: token,
  120. Group: strconv.Itoa(group),
  121. Addr: addr,
  122. }
  123. loginMap, err := loginLogin.ToMap()
  124. // loginMap := map[string]interface{}{
  125. // "LoginID": strconv.Itoa(int(loginaccount.LOGINID)),
  126. // "UserID": strconv.Itoa(int(loginaccount.USERID)),
  127. // "SessionID": strconv.Itoa(sessionID),
  128. // "Token": token,
  129. // "Group": strconv.Itoa(group),
  130. // "Addr": addr,
  131. // }
  132. if err != nil {
  133. global.M2A_LOG.Error("生成登录信息MAP失败", zap.Error(err))
  134. return
  135. }
  136. if err = j.SetRedisLogin(int(loginaccount.LOGINID), group, loginMap); err != nil {
  137. global.M2A_LOG.Error("Token写入Redis失败", zap.Error(err))
  138. return
  139. }
  140. // 生成旧登录服务Token
  141. // if err = j.SetOriRedisToken(int(loginaccount.LOGINID), group); err != nil {
  142. // // FIXME: 这里有类事务的回滚问题
  143. // global.M2A_LOG.Error("生成旧登录服务Token失败", zap.Error(err))
  144. // return
  145. // }
  146. return
  147. }