diff --git a/common/internalPb/internal.proto b/common/internalPb/internal.proto index 29d7bb8..af4e00f 100644 --- a/common/internalPb/internal.proto +++ b/common/internalPb/internal.proto @@ -11,11 +11,11 @@ enum MsgId message InternalMsg { - int32 service_name = 1; // 该服务类型下的具体的服务节点名,需要保证该消息是该服务节点发的。否则可能会导致客户端出现路由错误 - uint32 conn_id = 2; // 刚登陆时没有user_id,只有conn_id - int64 user_id = 3; // 玩家id - int32 msg_id = 4; // 消息id - bytes msg = 5; // 消息 + string service_name = 1; // 该服务类型下的具体的服务节点名,需要保证该消息是该服务节点发的。否则可能会导致客户端出现路由错误 + uint32 conn_id = 2; // 刚登陆时没有user_id,只有conn_id + int64 user_id = 3; // 玩家id + int32 msg_id = 4; // 消息id + bytes msg = 5; // 消息 } diff --git a/common/pb/code.proto b/common/pb/code.proto index 5ad23ec..47603a5 100644 --- a/common/pb/code.proto +++ b/common/pb/code.proto @@ -5,7 +5,12 @@ option go_package = "common/proto/pb"; enum ErrCode { OK = 0; - LoginDiffLoc = 1; // 帐号在其它地方登陆 + SystemErr = 1; // 系统错误 + LoginDiffLoc = 100; // 帐号在其它地方登陆 + LoginUserOrPwdErr = 102; // 帐号或密码错误 + AccountFrozen = 103; // 帐号已冻结 + AccountBanned = 104; // 帐号已封禁 + RegisterUserExist = 120; // 已有该帐号,无法注册 } diff --git a/common/pb/login.proto b/common/pb/login.proto index bf6c563..8912646 100644 --- a/common/pb/login.proto +++ b/common/pb/login.proto @@ -7,9 +7,11 @@ import "code.proto"; // 玩家登陆 message C2SUserLogin { - string username = 1; // 用户名 - string token = 2; // 密码或token - string version = 3; // 版本 + string username = 1; // 用户名 + string password = 2; // 密码或token + string ip = 3; + string device_id = 4; // 设备id + string version = 10; // 版本 } message S2CUserLogin diff --git a/common/proto/ipb/internal.pb.go b/common/proto/ipb/internal.pb.go index da21558..8637ba0 100644 --- a/common/proto/ipb/internal.pb.go +++ b/common/proto/ipb/internal.pb.go @@ -69,11 +69,11 @@ func (MsgId) EnumDescriptor() ([]byte, []int) { type InternalMsg struct { state protoimpl.MessageState `protogen:"open.v1"` - ServiceName int32 `protobuf:"varint,1,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` // 该服务类型下的具体的服务节点名,需要保证该消息是该服务节点发的。否则可能会导致客户端出现路由错误 - ConnId uint32 `protobuf:"varint,2,opt,name=conn_id,json=connId,proto3" json:"conn_id,omitempty"` // 刚登陆时没有user_id,只有conn_id - UserId int64 `protobuf:"varint,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` // 玩家id - MsgId int32 `protobuf:"varint,4,opt,name=msg_id,json=msgId,proto3" json:"msg_id,omitempty"` // 消息id - Msg []byte `protobuf:"bytes,5,opt,name=msg,proto3" json:"msg,omitempty"` // 消息 + ServiceName string `protobuf:"bytes,1,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` // 该服务类型下的具体的服务节点名,需要保证该消息是该服务节点发的。否则可能会导致客户端出现路由错误 + ConnId uint32 `protobuf:"varint,2,opt,name=conn_id,json=connId,proto3" json:"conn_id,omitempty"` // 刚登陆时没有user_id,只有conn_id + UserId int64 `protobuf:"varint,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` // 玩家id + MsgId int32 `protobuf:"varint,4,opt,name=msg_id,json=msgId,proto3" json:"msg_id,omitempty"` // 消息id + Msg []byte `protobuf:"bytes,5,opt,name=msg,proto3" json:"msg,omitempty"` // 消息 unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -108,11 +108,11 @@ func (*InternalMsg) Descriptor() ([]byte, []int) { return file_internal_proto_rawDescGZIP(), []int{0} } -func (x *InternalMsg) GetServiceName() int32 { +func (x *InternalMsg) GetServiceName() string { if x != nil { return x.ServiceName } - return 0 + return "" } func (x *InternalMsg) GetConnId() uint32 { @@ -149,7 +149,7 @@ const file_internal_proto_rawDesc = "" + "\n" + "\x0einternal.proto\x12\x03ipb\"\x8b\x01\n" + "\vInternalMsg\x12!\n" + - "\fservice_name\x18\x01 \x01(\x05R\vserviceName\x12\x17\n" + + "\fservice_name\x18\x01 \x01(\tR\vserviceName\x12\x17\n" + "\aconn_id\x18\x02 \x01(\rR\x06connId\x12\x17\n" + "\auser_id\x18\x03 \x01(\x03R\x06userId\x12\x15\n" + "\x06msg_id\x18\x04 \x01(\x05R\x05msgId\x12\x10\n" + diff --git a/common/proto/pb/code.pb.go b/common/proto/pb/code.pb.go index f328ee8..5c49227 100644 --- a/common/proto/pb/code.pb.go +++ b/common/proto/pb/code.pb.go @@ -24,19 +24,34 @@ const ( type ErrCode int32 const ( - ErrCode_OK ErrCode = 0 - ErrCode_LoginDiffLoc ErrCode = 1 // 帐号在其它地方登陆 + ErrCode_OK ErrCode = 0 + ErrCode_SystemErr ErrCode = 1 // 系统错误 + ErrCode_LoginDiffLoc ErrCode = 100 // 帐号在其它地方登陆 + ErrCode_LoginUserOrPwdErr ErrCode = 102 // 帐号或密码错误 + ErrCode_AccountFrozen ErrCode = 103 // 帐号已冻结 + ErrCode_AccountBanned ErrCode = 104 // 帐号已封禁 + ErrCode_RegisterUserExist ErrCode = 120 // 已有该帐号,无法注册 ) // Enum value maps for ErrCode. var ( ErrCode_name = map[int32]string{ - 0: "OK", - 1: "LoginDiffLoc", + 0: "OK", + 1: "SystemErr", + 100: "LoginDiffLoc", + 102: "LoginUserOrPwdErr", + 103: "AccountFrozen", + 104: "AccountBanned", + 120: "RegisterUserExist", } ErrCode_value = map[string]int32{ - "OK": 0, - "LoginDiffLoc": 1, + "OK": 0, + "SystemErr": 1, + "LoginDiffLoc": 100, + "LoginUserOrPwdErr": 102, + "AccountFrozen": 103, + "AccountBanned": 104, + "RegisterUserExist": 120, } ) @@ -72,10 +87,15 @@ var File_code_proto protoreflect.FileDescriptor const file_code_proto_rawDesc = "" + "\n" + "\n" + - "code.proto\x12\x02pb*#\n" + + "code.proto\x12\x02pb*\x86\x01\n" + "\aErrCode\x12\x06\n" + - "\x02OK\x10\x00\x12\x10\n" + - "\fLoginDiffLoc\x10\x01B\x11Z\x0fcommon/proto/pbb\x06proto3" + "\x02OK\x10\x00\x12\r\n" + + "\tSystemErr\x10\x01\x12\x10\n" + + "\fLoginDiffLoc\x10d\x12\x15\n" + + "\x11LoginUserOrPwdErr\x10f\x12\x11\n" + + "\rAccountFrozen\x10g\x12\x11\n" + + "\rAccountBanned\x10h\x12\x15\n" + + "\x11RegisterUserExist\x10xB\x11Z\x0fcommon/proto/pbb\x06proto3" var ( file_code_proto_rawDescOnce sync.Once diff --git a/common/proto/pb/login.pb.go b/common/proto/pb/login.pb.go index 97dc577..069c7ca 100644 --- a/common/proto/pb/login.pb.go +++ b/common/proto/pb/login.pb.go @@ -25,8 +25,10 @@ const ( type C2SUserLogin struct { state protoimpl.MessageState `protogen:"open.v1"` Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` // 用户名 - Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` // 密码或token - Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` // 版本 + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` // 密码或token + Ip string `protobuf:"bytes,3,opt,name=ip,proto3" json:"ip,omitempty"` + DeviceId string `protobuf:"bytes,4,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` // 设备id + Version string `protobuf:"bytes,10,opt,name=version,proto3" json:"version,omitempty"` // 版本 unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -68,9 +70,23 @@ func (x *C2SUserLogin) GetUsername() string { return "" } -func (x *C2SUserLogin) GetToken() string { +func (x *C2SUserLogin) GetPassword() string { if x != nil { - return x.Token + return x.Password + } + return "" +} + +func (x *C2SUserLogin) GetIp() string { + if x != nil { + return x.Ip + } + return "" +} + +func (x *C2SUserLogin) GetDeviceId() string { + if x != nil { + return x.DeviceId } return "" } @@ -318,11 +334,14 @@ var File_login_proto protoreflect.FileDescriptor const file_login_proto_rawDesc = "" + "\n" + "\vlogin.proto\x12\x02pb\x1a\n" + - "code.proto\"Z\n" + + "code.proto\"\x8d\x01\n" + "\fC2SUserLogin\x12\x1a\n" + - "\busername\x18\x01 \x01(\tR\busername\x12\x14\n" + - "\x05token\x18\x02 \x01(\tR\x05token\x12\x18\n" + - "\aversion\x18\x03 \x01(\tR\aversion\"^\n" + + "\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" + + "\bpassword\x18\x02 \x01(\tR\bpassword\x12\x0e\n" + + "\x02ip\x18\x03 \x01(\tR\x02ip\x12\x1b\n" + + "\tdevice_id\x18\x04 \x01(\tR\bdeviceId\x12\x18\n" + + "\aversion\x18\n" + + " \x01(\tR\aversion\"^\n" + "\fS2CUserLogin\x12\x1f\n" + "\x04code\x18\x01 \x01(\x0e2\v.pb.ErrCodeR\x04code\x12\x17\n" + "\auser_id\x18\x02 \x01(\x03R\x06userId\x12\x14\n" + diff --git a/server/gate/server/processor.go b/server/gate/server/processor.go index f8d8351..8b9b7e2 100644 --- a/server/gate/server/processor.go +++ b/server/gate/server/processor.go @@ -1,6 +1,7 @@ package server import ( + "game/common/proto/ipb" "game/common/proto/pb" "game/common/topicName" "github.com/fox/fox/processor" @@ -19,16 +20,16 @@ func (s *GateService) initProcessor() { } // 收到登陆成功消息,判断是否顶号 -func (s *GateService) onUserLogin(conn ws.IConn, msg *pb.S2CUserLogin) { +func (s *GateService) onUserLogin(iMsg *ipb.InternalMsg, conn ws.IConn, msg *pb.S2CUserLogin) { if conn == nil { return } // 登陆失败,回传玩家 if msg.Code != pb.ErrCode_OK { - s.SendClientMsg(conn, int32(pb.MsgId_S2CUserLoginId), msg) + s.SendClientMsg(conn, iMsg.ServiceName, int32(pb.MsgId_S2CUserLoginId), msg) return } - s.SendClientMsg(conn, int32(pb.MsgId_S2CUserLoginId), msg) + s.SendClientMsg(conn, iMsg.ServiceName, int32(pb.MsgId_S2CUserLoginId), msg) s.wss.SetUserId(conn.Id(), msg.UserId) sName := s.bindService.LoadFromRedis(conn.UserId(), pb.ServiceTypeId_STI_Gate) // 网关不同,说明玩家在其它网关上登陆, @@ -40,11 +41,11 @@ func (s *GateService) onUserLogin(conn ws.IConn, msg *pb.S2CUserLogin) { } // 收到登出消息 -func (s *GateService) onUserLogout(conn ws.IConn, msg *pb.S2CUserLogout) { +func (s *GateService) onUserLogout(iMsg *ipb.InternalMsg, conn ws.IConn, msg *pb.S2CUserLogout) { if conn == nil { return } - s.SendClientMsg(conn, int32(pb.MsgId_S2CUserLogoutId), msg) + s.SendClientMsg(conn, iMsg.ServiceName, int32(pb.MsgId_S2CUserLogoutId), msg) // 登出的清理工作由WsOnDisconnect实现 conn.NotifyClose() } diff --git a/server/gate/server/service.go b/server/gate/server/service.go index 1173537..b059db2 100644 --- a/server/gate/server/service.go +++ b/server/gate/server/service.go @@ -123,9 +123,9 @@ func (s *GateService) findConn(msg *ipb.InternalMsg) ws.IConn { // 处理消息 func (s *GateService) connMessage(conn ws.IConn, iMsg *ipb.InternalMsg) { if req, err := s.processor.Unmarshal(iMsg.MsgId, iMsg.Msg); err == nil { - err = s.processor.Dispatch(iMsg.MsgId, conn, req) + err = s.processor.Dispatch(iMsg.MsgId, iMsg, conn, req) } else { - s.SendClientData(conn, iMsg.MsgId, iMsg.Msg) + s.SendClientData(conn, iMsg.ServiceName, iMsg.MsgId, iMsg.Msg) } //log.Debug(s.Log("on message:%v", string(msg))) } @@ -185,32 +185,28 @@ func (s *GateService) WsOnMessage(conn ws.IConn, data []byte) { // 向内部服务发送消息 func (s *GateService) SendServiceData(topic string, conn ws.IConn, msgId int32, data []byte) { - iMsg := &ipb.InternalMsg{ConnId: conn.Id(), UserId: conn.UserId(), MsgId: msgId, Msg: data} + iMsg := &ipb.InternalMsg{ServiceName: s.Name(), ConnId: conn.Id(), UserId: conn.UserId(), MsgId: msgId, Msg: data} dMsg, _ := proto.Marshal(iMsg) _ = s.Send(topic, dMsg) } // 向内部服务发送消息 func (s *GateService) SendServiceMsg(topic string, conn ws.IConn, msgId int32, msg proto.Message) { - iMsg := &ipb.InternalMsg{ConnId: conn.Id(), UserId: conn.UserId(), MsgId: msgId} - iMsg.Msg, _ = proto.Marshal(msg) - dMsg, _ := proto.Marshal(iMsg) - _ = s.Send(topic, dMsg) + data, _ := proto.Marshal(msg) + s.SendServiceData(topic, conn, msgId, data) } // 向玩家发送消息 -func (s *GateService) SendClientData(conn ws.IConn, msgId int32, data []byte) { - cMsg := &pb.ClientMsg{MsgId: msgId, Data: data} +func (s *GateService) SendClientData(conn ws.IConn, serviceName string, msgId int32, data []byte) { + cMsg := &pb.ClientMsg{ServiceName: serviceName, MsgId: msgId, Data: data} dMsg, _ := proto.Marshal(cMsg) _ = conn.SendMsg(dMsg) } // 向玩家发送消息 -func (s *GateService) SendClientMsg(conn ws.IConn, msgId int32, msg proto.Message) { - cMsg := &pb.ClientMsg{MsgId: msgId} - cMsg.Data, _ = proto.Marshal(msg) - dMsg, _ := proto.Marshal(cMsg) - _ = conn.SendMsg(dMsg) +func (s *GateService) SendClientMsg(conn ws.IConn, serviceName string, msgId int32, msg proto.Message) { + data, _ := proto.Marshal(msg) + s.SendClientData(conn, serviceName, msgId, data) } func (s *GateService) WsOnDisconnect(conn ws.IConn) { diff --git a/server/login/model/userAccount.go b/server/login/model/userAccount.go index 597b32e..be35fad 100644 --- a/server/login/model/userAccount.go +++ b/server/login/model/userAccount.go @@ -94,6 +94,39 @@ func (s *UserLoginOp) Login(username, password, ip, deviceID string) (*UserAccou return &user, err } +// 注册新用户 +func (s *UserLoginOp) RegisterNewUser(username, password, ip, deviceID string) (*UserAccount, error) { + // 密码加密 + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return nil, err + } + + user := UserAccount{ + Username: username, + Password: string(hashedPassword), + DeviceID: deviceID, + RegisterIP: ip, + Status: 1, + LastLoginIP: ip, + } + + if err := s.db.Create(&user).Error; err != nil { + return nil, err + } + + s.recordLoginLog(user.ID, ip, deviceID, true, "") + + // 生成访问令牌 + token, err := generateToken(user.ID, user.Username) + if err != nil { + return nil, err + } + user.Password = token + + return &user, nil +} + // 记录登录日志 func (s *UserLoginOp) recordLoginLog(userID uint, ip, deviceID string, success bool, failReason string) { logEntry := UserLoginLog{ @@ -111,6 +144,8 @@ func (s *UserLoginOp) recordLoginLog(userID uint, ip, deviceID string, success b // 生成JWT令牌(简化版) func generateToken(userID uint, username string) (string, error) { + _ = userID + _ = username // 这里应该使用JWT库生成实际令牌 // 简化实现,实际项目中请使用安全的JWT实现 return "generated-token-placeholder", nil diff --git a/server/login/server/processor.go b/server/login/server/processor.go index 832c942..233812e 100644 --- a/server/login/server/processor.go +++ b/server/login/server/processor.go @@ -1,93 +1,57 @@ package server import ( + "errors" + "game/common/proto/ipb" "game/common/proto/pb" - "game/common/topicName" - "github.com/fox/fox/log" + "game/server/login/model" "github.com/fox/fox/processor" "github.com/fox/fox/service" - "golang.org/x/crypto/bcrypt" "gorm.io/gorm" - "time" ) func (s *LoginService) initProcessor() { s.processor.RegisterMessages(processor.RegisterMetas{ - //pb.MsgId_C2SChatId: {pb.C2SChat{}, s.onChat}, + pb.MsgId_C2SUserLogoutId: {pb.C2SUserLogin{}, s.onLoginOrRegister}, }) } -// 登录或注册 -func (s *LoginService) LoginOrRegister(req *pb.C2SUserLogin) { - // 1. 尝试查找用户 - var user models.UserAccount - err := s.db.Where("username = ?", req.Username).First(&user).Error - +func (s *LoginService) checkLoginOrRegister(req *pb.C2SUserLogin) (user *model.UserAccount, code pb.ErrCode) { + op := model.NewUserLoginOp() + var err error + user, err = op.Login(req.Username, req.Password, req.Ip, req.DeviceId) if err != nil { - // 如果是用户不存在错误,则注册新用户 if errors.Is(err, gorm.ErrRecordNotFound) { - return s.registerNewUser(req) - } - // 其他数据库错误 - return nil, err - } - - // 2. 验证密码 - if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)); err != nil { - // 记录失败登录日志 - s.recordLoginLog(user.ID, req.IP, req.DeviceID, false, "密码错误") - return nil, errors.New("用户名或密码错误") - } - - // 3. 检查账号状态 - if user.Status != 1 { - var reason string - switch user.Status { - case 2: - reason = "账号已冻结" - case 3: - reason = "账号已封禁" - } - s.recordLoginLog(user.ID, req.IP, req.DeviceID, false, reason) - return nil, errors.New(reason) - } - - // 4. 更新最后登录信息 - user.LastLoginIP = req.IP - user.LastLoginTime = time.Now() - if err := s.db.Save(&user).Error; err != nil { - return nil, err - } - - // 5. 记录成功登录日志 - s.recordLoginLog(user.ID, req.IP, req.DeviceID, true, "") - - // 6. 生成访问令牌 - token, err := generateToken(user.ID, user.Username) - if err != nil { - return nil, err - } - - return &models.LoginResponse{ - UserID: user.ID, - Username: user.Username, - Token: token, - }, nil -} - -// 收到登陆成功消息,判断是否顶号 -func (s *LoginService) onLogin(uid int64, msg *pb.C2SUserLogin) { - - switch msg.Type { - case pb.ChatType_CT_Private: - sName, err := s.bindService.FindServiceName(msg.DstUser.UserId, pb.ServiceTypeId_STI_Gate) - if err != nil { - log.DebugF("find user:%v in gate err: %v", uid, err) + user, err = op.RegisterNewUser(req.Username, req.Password, req.Ip, req.DeviceId) + if err != nil { + code = pb.ErrCode_RegisterUserExist + return + } + } else if errors.Is(err, model.ErrUserOrPassword) { + code = pb.ErrCode_LoginUserOrPwdErr return + } else if errors.Is(err, model.ErrAccountFrozen) { + code = pb.ErrCode_AccountFrozen + return + } else if errors.Is(err, model.ErrAccountBanned) { + code = pb.ErrCode_AccountBanned + return + } else { + code = pb.ErrCode_SystemErr } - s.SendServiceMsg(service.TopicEx(sName), msg.DstUser.UserId, int32(pb.MsgId_S2CChatId), msg) - default: - s.SendServiceMsg(service.TopicEx(topicName.WorldMessage), uid, int32(pb.MsgId_S2CChatId), msg) } - + return user, code +} + +// 登录或注册 +func (s *LoginService) onLoginOrRegister(iMsg *ipb.InternalMsg, req *pb.C2SUserLogin) { + user, code := s.checkLoginOrRegister(req) + userId := int64(0) + rsp := &pb.S2CUserLogin{Code: code} + if user != nil && code == pb.ErrCode_OK { + rsp.UserId = int64(user.ID) + rsp.Token = user.Password + userId = rsp.UserId + } + s.SendServiceMsg(service.TopicEx(iMsg.ServiceName), iMsg.ConnId, userId, int32(pb.MsgId_S2CUserLoginId), rsp) } diff --git a/server/login/server/service.go b/server/login/server/service.go index d0fa908..12a9036 100644 --- a/server/login/server/service.go +++ b/server/login/server/service.go @@ -93,22 +93,22 @@ func (s *LoginService) OnMessage(data []byte) error { return err } if req, err := s.processor.Unmarshal(iMsg.MsgId, iMsg.Msg); err == nil { - err = s.processor.Dispatch(iMsg.MsgId, iMsg.UserId, req) + err = s.processor.Dispatch(iMsg.MsgId, iMsg, req) } //log.Debug(s.Log("on message:%v", string(msg))) return nil } // 向内部服务发送消息 -func (s *LoginService) SendServiceData(topic string, userId int64, msgId int32, data []byte) { - iMsg := &ipb.InternalMsg{ConnId: 0, UserId: userId, MsgId: msgId, Msg: data} +func (s *LoginService) SendServiceData(topic string, connId uint32, userId int64, msgId int32, data []byte) { + iMsg := &ipb.InternalMsg{ConnId: connId, UserId: userId, MsgId: msgId, Msg: data} dMsg, _ := proto.Marshal(iMsg) _ = s.Send(topic, dMsg) } // 向内部服务发送消息 -func (s *LoginService) SendServiceMsg(topic string, userId int64, msgId int32, msg proto.Message) { - iMsg := &ipb.InternalMsg{ConnId: 0, UserId: userId, MsgId: msgId} +func (s *LoginService) SendServiceMsg(topic string, connId uint32, userId int64, msgId int32, msg proto.Message) { + iMsg := &ipb.InternalMsg{ConnId: connId, UserId: userId, MsgId: msgId} iMsg.Msg, _ = proto.Marshal(msg) dMsg, _ := proto.Marshal(iMsg) _ = s.Send(topic, dMsg)