木匣子

Web/Game/Programming/Life etc.

手机网游的用户中心设计

在开发手机网游的过程中,为玩家建立一个用户中心,比起直接在游戏服务器上直接处理玩家登录,将会带来很多益处。用户中心负责管理独立于游戏数据的玩家帐号信息。并且一个玩家可以在多个服务器上甚至同一品牌下的多个游戏有不同的游戏帐号,当游戏服务器不断增加时,用户可以集中管理。同时这也符合单一职责原则。

此外,由于是手机网游,通常玩家不会愿意为注册帐号花太多时间。很多游戏引入了第三方平台 SDK,可以使用第三方帐号登录游戏;甚至支持直接产生临时帐号进行游客登录,等到玩家消费或认可游戏后再进行帐号绑定。我们要如何设计一个系统来支持这些功能呢?

0x01 USERNAME : PASSWORK

既然是用户中心,首先来看看最一般情况的登录模型:

username: _________
password: _________

[REGISTER]  [LOGIN]

玩家打开游戏后,显示登录框,可以注册或直接登录游戏;
这里需要用户中心提供一些 API 来完成登录过程。

所以 API 应该是什么样子的呢?输入是什么,输出又是什么?可以肯定的是我们要把用户名密码提交到用户中心,用户中心将返回登录(或注册)的结果

另外在 API 中我们可以提供一些冗余字段为之后的扩展做一些准备,例如登录类型(普通、SDK、游客),平台参数(来自哪个SDK),目标游戏(登录哪个游戏)等。

而返回结果是什么呢?登录成功,或是失败以及失败的原因——但这并不是一个让玩家记住密码的游戏——我们需要将登录成果反馈给游戏服务器,从而进入游戏世界。如何实现这一过程?

当玩家输入错误的用户名及密码时,我们只需要进行友好的提示即可。如果玩家登录成功,客户端会得到一个令牌(TOKEN),在接下来的过程中,客户端使用这个令牌向游戏大厅请求服务器列表,最终使用这个令牌登陆游戏服务器。

TOKEN

令牌可以看作是一种协议,可以用对称或非对称加密的形式将登录成功的信息封装起来,例如令牌的有效期玩家ID 等。这些信息对玩家是透明的,客户端只是得到一个加密串。然而用户中心与游戏服务器之间共享着能够正确解密这个加密串的钥匙,所以游戏服务器最终能知道玩家的ID是否来自用户中心合法的授权。对于不同的游戏平台,可以使用不同的密钥,避免不合法的访问。

LOGIN API

user_center/login

参数

  • username = hello
  • password = *****
  • platform = iOS/Android

成功

  • state = ok
  • msg = *T*O*K*E*N*

失败

  • state = error
  • msg = user does not exist or password is wrong

REGISTER API

user_center/register

参数

  • username = hello
  • password = *****

成功

  • state = ok
  • msg = *T*O*K*E*N*

失败

  • state = error
  • msg = username is taken

0x02 SDK : UID : TOKEN

在手机游戏中,使用第三方平台 SDK 登录也是非常常见的:

[LOGIN with GOOGLE]
[LOGIN with WECHAT]
[LOGIN with WEIBO ]

假若用户已经拥有第三方平台的帐号,而我们在游戏中接入了相应的平台。那么我们就可以使用平台提供的 SDK 进行用户登录。登录的过程由 SDK 提供,可能的形式各不相同:

  • 在弹出的窗口中完成帐号登录
  • 跳转至特定的应用中完成授权

其本质上都是基于 OAuth 协议完成的用户授权。客户端能够得到 SDK 返回的 SDK-UID (用户标识)以及 SDK-TOKEN ,而后我们将这些信息发送到用户中心进行登录。

用户中心根据 SDK 平台标识、SDK-UID 以及 SDK-TOKEN 向 SDK 的 OAuth 服务器发起验证,确认 SDK-UID 的合法性后,返回登录结果。

SDK LOGIN API

user_center/sdk_login

参数

  • uid = 10000
  • sdk-token = *S*D*K*T*O*K*E*N*
  • platform = google/wechat/weibo/4399/TongBu/91/…

成功

  • state = ok
  • msg = *T*O*K*E*N*

失败

  • state = error
  • msg = sdk token is not correct

0x03 GUEST

游客登录提供了更加便捷的登录方式,让那些并不想注册的玩家也有机会体验游戏:

[LOGIN AS GUEST]

玩家点击游戏客录,若本地存有上一次登录的游客令牌,则使用该游客令牌进行登录。若没有,则直接登录,并保存获得的游客令牌,供下次使用。

这种方式的缺点是不安全,玩家可以伪造其它游客的令牌从而进入游戏(可采用更加随机的方式才生令牌,避免预测);游戏删除或存档丢失后无法再登录原来的帐号;并且无法在其它设备上使用该帐号。

GUEST LOGIN API

user_center/guest

参数

  • guest-id = *G*U*E*S*T*I*D*

成功

  • state = ok
  • msg = *T*O*K*E*N*

新游客

  • state = new
  • msg = *T*O*K*E*N*
  • guest-id = *G*U*E*S*T*I*D*

0x04 BIND

帐号绑定是一个向第三方 SDK 平台的玩家,或者游客提供的功能:

SDK-ID: 001 / GUEST

username: _________
password: _________

[REGISTER and BIND]

绑定后,游客将变成注册用户,可以使用用户名和密码登录,这意味着玩家可以在不同的设备上使用该帐号。

当第三方 SDK 平台出现网络故障的时候,会导致玩家不能进入游戏,这是很尴尬的局面,但是如果玩家绑定了帐号,则可以尝试使用用户名和密码进入同一个游戏帐号。

Bind API

user_center/bind

参数

  • token = *T*O*K*E*N
  • username = hello
  • password = *****
  • platform = iOS/Android

成功

  • state = ok

失败

  • state = error
  • msg = user already bind with another account

0x05 DATABASE

定义了这些功能和接口,我们可以对数据库进行设计。看看需要哪些字段

            TYPE        KEY    IS_NULL
uid         AUTO_INC    YES    NO
username    VARCHAR     -      YES
password    VARCHAR     -      YES
sdk         VARCHAR     -      YES
sdk_uid     VARCHAR     -      YES
sdk_token   VARCHAR     -      YES
guest_id    VARCHAR     -      YES

虽然之前的 API 都没有出现 uid 但这个字段是必须的,游戏服务器通过它与用户中心里的帐号关联起来,uid 会被加密到令牌中,通过客户端发送给游戏服务器。

若 guest_id 不为空,则该用户是游客,可以匹配客户端发送的 guest-id 进行登录,否则需要通过其它两种方式进行登录。而 username 和 sdk 可以同时存在,也可以独立存在。

0x06 MORE

用户中心是否需要做权限管理:例如增加权限位,设置 GM 或测试人员,以及封禁帐号?如果这个用户中心只为一个游戏服务,那么完全可以加上这些功能,甚至提供一些游戏大厅的功能:根据平台和权限分配服务器列表等等;如果这个用户中心将服务于多款游戏,那么最好将这些功能分离到另外的游戏大厅或网关服务器去实现。至此用户中心的职责就明了了。