| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- package account
- import (
- "encoding/base64"
- "encoding/hex"
- "errors"
- "fmt"
- "mtp20access/client"
- "mtp20access/global"
- accountModel "mtp20access/model/account"
- "mtp20access/model/account/request"
- "mtp20access/packet"
- "mtp20access/rabbitmq"
- "mtp20access/res/pb"
- "sync"
- "mtp20access/utils"
- "strconv"
- commonRequest "mtp20access/model/common/request"
- "github.com/gin-gonic/gin"
- "github.com/gofrs/uuid"
- "github.com/golang/protobuf/proto"
- "go.uber.org/zap"
- )
- var (
- mtx sync.RWMutex
- curSessionID int = 90000 // 本服务SessionID从90000开始,以避免与旧登录服务重叠
- )
- // Login 用户登录
- func Login(req request.LoginReq, addr string) (loginaccount *accountModel.Loginaccount, token string, sessionID int, expiresAt int64, err error) {
- // 分别尝试用LoginID、LoginCode和手机号码进行登录
- loginaccount, _, err = getLoginAccount(req.UserName, req.Password)
- if err != nil {
- return
- }
- // 判断用户状态
- if loginaccount.LOGINSTATUS == 2 {
- err = errors.New("账户已冻结")
- return
- }
- if loginaccount.LOGINSTATUS == 3 {
- err = errors.New("账户已注销")
- return
- }
- // 生成Token,并写入Redis
- token, expiresAt, sessionID, err = buildRedisLoginInfo(*loginaccount, addr, req.ClientType)
- if err != nil {
- return
- }
- // 发送登录报文给总线
- loginReq := pb.LoginReq{}
- loginReq.LoginID = utils.SetPointValue(uint64(loginaccount.LOGINID))
- // loginReq.LoginPWD = utils.SetPointValue(strings.ToLower(utils.EncoderSha256(fmt.Sprintf("%v%s", loginReq.LoginID, oriPwd))))
- loginReq.LoginPWD = utils.SetPointValue(loginaccount.PASSWORD)
- loginReq.LoginIp = utils.SetPointValue(addr)
- loginReq.LoginType = utils.SetPointValue[uint32](0)
- loginReq.ClientType = utils.SetPointValue(uint32(req.ClientType))
- uid, _ := uuid.NewV4()
- loginReq.GUID = utils.SetPointValue(uid.String())
- loginReq.Version = utils.SetPointValue("1.0.0")
- uid, _ = uuid.NewV4()
- loginReq.DeviceID = utils.SetPointValue(uid.String())
- loginReq.ClientAppID = utils.SetPointValue("MTP20_GO_ACCESS")
- header := pb.MessageHead{}
- header.FunCode = utils.SetPointValue(uint32(global.LoginReq))
- uid, _ = uuid.NewV4()
- header.UUID = utils.SetPointValue(uid.String())
- loginReq.Header = &header
- if b, e := proto.Marshal(&loginReq); e == nil {
- packet := &client.MQPacket{
- FunCode: uint32(global.LoginReq),
- SessionId: uint32(sessionID),
- Data: &b,
- }
- go rabbitmq.Publish(global.TOPIC_REQ_USER, packet)
- }
- return
- }
- // getLoginAccount 分别尝试用LoginID、LoginCode和手机号码进行登录
- func getLoginAccount(userName string, password string) (loginaccount *accountModel.Loginaccount, oriPwd string, err error) {
- // 密码解密(5.0报文解密)
- d, err := base64.StdEncoding.DecodeString(password)
- if err != nil {
- return
- }
- d1 := d[4 : len(d)-8] // 解密时要去头尾
- p, err := packet.Decrypt(d1, packet.AESKey, true)
- if err != nil {
- return
- }
- oriPwd = string(p)
- // 通过LoginID查询
- if loginID, _ := strconv.Atoi(userName); loginID != 0 {
- loginaccount = &accountModel.Loginaccount{
- LOGINID: int64(loginID),
- PASSWORD: utils.EncoderSha256(fmt.Sprintf("%s%s", userName, oriPwd)), // 构建数据库存储的密码
- }
- if has, _ := global.M2A_DB.Get(loginaccount); has {
- return
- }
- }
- // 通过LoginCode查询
- loginaccount = &accountModel.Loginaccount{
- LOGINCODE: userName,
- }
- if has, _ := global.M2A_DB.Get(loginaccount); has {
- // 构建数据库存储的密码
- if loginaccount.PASSWORD == utils.EncoderSha256(fmt.Sprintf("%d%s", loginaccount.LOGINID, oriPwd)) {
- return
- }
- }
- // 通过手机号码查询,需要AES加密
- key, _ := hex.DecodeString(utils.AESSecretKey)
- if mobileEncrypted, _ := utils.AESEncrypt([]byte(userName), key); mobileEncrypted != nil {
- // 从三方表获取LoginID
- userauthinfo := &accountModel.Userauthinfo{
- AUTHID: hex.EncodeToString(mobileEncrypted),
- AUTHTYPE: 3,
- }
- if has, _ := global.M2A_DB.Get(userauthinfo); has {
- loginaccount = &accountModel.Loginaccount{
- LOGINID: userauthinfo.LOGINID,
- PASSWORD: utils.EncoderSha256(fmt.Sprintf("%v%s", userauthinfo.LOGINID, oriPwd)), // 构建数据库存储的密码
- }
- if has, _ := global.M2A_DB.Get(loginaccount); has {
- return
- }
- }
- // loginaccount = &accountModel.Loginaccount{
- // MOBILE: string(mobileEncrypted),
- // }
- // if has, _ := global.M2A_DB.Get(loginaccount); has {
- // // 构建数据库存储的密码
- // if loginaccount.PASSWORD == utils.EncoderSha256(fmt.Sprintf("%d%s", loginaccount.LOGINID, pwd)) {
- // return
- // }
- // }
- }
- err = errors.New("错误的用户名或密码")
- return
- }
- // newSessionID 获取
- func newSessionID() int {
- mtx.RLock()
- defer mtx.RUnlock()
- curSessionID += 1
- return curSessionID
- }
- // buildRedisLoginInfo 生成Token,并写入Redis
- func buildRedisLoginInfo(loginaccount accountModel.Loginaccount, addr string, group int) (token string, expiresAt int64, sessionID int, err error) {
- // 生成SessionID
- sessionID = newSessionID()
- // 生成本服务Token
- j := &utils.JWT{SigningKey: []byte(global.M2A_CONFIG.JWT.SigningKey)} // 唯一签名
- claims := j.CreateClaims(commonRequest.BaseClaims{
- LoginID: int(loginaccount.LOGINID),
- Group: group,
- SessionID: sessionID,
- UserID: int(loginaccount.USERID),
- })
- token, err = j.CreateToken(claims)
- if err != nil {
- global.M2A_LOG.Error("生成本服token失败", zap.Error(err))
- return
- }
- expiresAt = claims.RegisteredClaims.ExpiresAt.Unix()
- loginLogin := client.LoginRedis{
- LoginID: strconv.Itoa(int(loginaccount.LOGINID)),
- UserID: strconv.Itoa(int(loginaccount.USERID)),
- SessionID: strconv.Itoa(sessionID),
- Token: token,
- Group: strconv.Itoa(group),
- Addr: addr,
- }
- loginMap, err := loginLogin.ToMap()
- // loginMap := map[string]interface{}{
- // "LoginID": strconv.Itoa(int(loginaccount.LOGINID)),
- // "UserID": strconv.Itoa(int(loginaccount.USERID)),
- // "SessionID": strconv.Itoa(sessionID),
- // "Token": token,
- // "Group": strconv.Itoa(group),
- // "Addr": addr,
- // }
- if err != nil {
- global.M2A_LOG.Error("生成登录信息MAP失败", zap.Error(err))
- return
- }
- if err = j.SetRedisLogin(int(loginaccount.LOGINID), group, loginMap); err != nil {
- global.M2A_LOG.Error("Token写入Redis失败", zap.Error(err))
- return
- }
- // 生成旧登录服务Token
- // if err = j.SetOriRedisToken(int(loginaccount.LOGINID), group); err != nil {
- // // FIXME: 这里有类事务的回滚问题
- // global.M2A_LOG.Error("生成旧登录服务Token失败", zap.Error(err))
- // return
- // }
- // 记录用户信息
- mtx.Lock()
- defer mtx.Unlock()
- if client.Clients == nil {
- client.Clients = make(map[int]*client.Client, 0)
- }
- // 这里应该按loginid和group把之前的client删除掉
- // delete(client.Clients, s)
- targetKeys := make([]int, 0)
- for key, item := range client.Clients {
- if item.LoginID == loginLogin.LoginID && item.Group == loginLogin.Group {
- targetKeys = append(targetKeys, key)
- }
- }
- for _, k := range targetKeys {
- delete(client.Clients, k)
- }
- client.Clients[claims.SessionID] = &client.Client{LoginRedis: loginLogin}
- return
- }
- // RestoreLoginWithToken 通过Token检验恢复登录状态失败
- func RestoreLoginWithToken(loginID int, group int, token string) (err error) {
- // 先判断Clients中是否有对应的对像
- if client.Clients != nil {
- targetKeys := make([]int, 0)
- for key, item := range client.Clients {
- if item.LoginID == strconv.Itoa(loginID) && item.Group == strconv.Itoa(group) {
- targetKeys = append(targetKeys, key)
- }
- }
- if len(targetKeys) > 0 {
- return
- }
- }
- // 没有的话重新创建一个
- // 从Redis获取登录信息
- j := utils.NewJWT()
- loginMap, err := j.GetRedisLogin(loginID, group)
- if err != nil {
- global.M2A_LOG.Error("Token检验恢复登录状态失败", zap.Error(err))
- return
- }
- loginId := loginMap["loginId"]
- userId := loginMap["userId"]
- sessionId := loginMap["sessionId"]
- addr := loginMap["addr"]
- loginLogin := client.LoginRedis{
- LoginID: loginId,
- UserID: userId,
- SessionID: sessionId,
- Token: token,
- Group: strconv.Itoa(group),
- Addr: addr,
- }
- // 记录用户信息
- mtx.Lock()
- defer mtx.Unlock()
- if client.Clients == nil {
- client.Clients = make(map[int]*client.Client, 0)
- }
- s, err := strconv.Atoi(sessionId)
- if err != nil {
- global.M2A_LOG.Error("Token检验恢复登录状态失败", zap.Error(err))
- return
- }
- client.Clients[s] = &client.Client{LoginRedis: loginLogin}
- return
- }
- // GetClientsByAccountID 通过资金账户获取所有的
- func GetClientsByAccountID(accountID uint64) (clients []*client.Client, err error) {
- clients = make([]*client.Client, 0)
- loginIds := make([]string, 0)
- sql := fmt.Sprintf(`
- SELECT
- to_char(t.loginid)
- FROM loginaccount t
- INNER JOIN taaccount a ON a.userid = t.userid
- WHERE a.accountid = %v
- `, accountID)
- if err = global.M2A_DB.SQL(sql).Find(&loginIds); err != nil {
- global.M2A_LOG.Info("获取LoginID失败", zap.Error(err))
- return
- } else {
- global.M2A_LOG.Info("获取LoginID", zap.Any("loginIds", loginIds))
- }
- var mtx sync.RWMutex
- mtx.Lock()
- defer mtx.Unlock()
- if len(loginIds) > 0 && len(client.Clients) > 0 {
- for _, item := range loginIds {
- for _, client := range client.Clients {
- // c := client.Clients[i]
- if client.LoginID == item {
- clients = append(clients, client)
- }
- }
- }
- }
- return
- }
- func Logout(c *gin.Context) (err error) {
- // 获取请求账号信息
- s, exists := c.Get("claims")
- if !exists {
- err = errors.New("获取请求账号信息异常")
- global.M2A_LOG.Error(err.Error(), zap.Error(err))
- return
- }
- claims := s.(*commonRequest.CustomClaims)
- // 获取登录账户信息
- t, exists := client.Clients[claims.SessionID]
- if !exists {
- err = errors.New("获取登录账户信息异常")
- global.M2A_LOG.Error(err.Error(), zap.Any("SessionID", claims.SessionID), zap.Error(err))
- return
- }
- // 发送登出报文给总线
- logoutReq := pb.LogoutReq{}
- logoutReq.LoginID = utils.SetPointValue(uint64(claims.LoginID))
- logoutReq.Token = utils.SetPointValue(t.OldToken)
- logoutReq.LoginIp = utils.SetPointValue(c.ClientIP())
- port, _ := strconv.Atoi(c.Request.URL.Port())
- logoutReq.LoginPort = utils.SetPointValue(uint32(port))
- header := pb.MessageHead{}
- header.FunCode = utils.SetPointValue(uint32(global.LogoutReq))
- userID, _ := strconv.Atoi(t.UserID)
- header.UserID = utils.SetPointValue(uint32(userID))
- uid, _ := uuid.NewV4()
- header.UUID = utils.SetPointValue(uid.String())
- logoutReq.Header = &header
- if b, e := proto.Marshal(&logoutReq); e == nil {
- packet := &client.MQPacket{
- FunCode: uint32(global.LogoutReq),
- SessionId: uint32(claims.SessionID),
- Data: &b,
- }
- go rabbitmq.Publish(global.TOPIC_REQ_USER, packet)
- }
- return
- }
|