使用OpenID Connect实现Unity应用的用户登录【理论篇】

这次,我们来搞点好玩的事情:给我们的Unity游戏和应用实现一个原生的用户登录、注册的功能。

通常情况下,我们玩到的游戏是怎么做用户登录的呢。一种方法就是,在启动器里完成用户登录,之后把相关的token通过启动参数传递给游戏本体,这种做法在不少PC游戏中是非常常见的。

另一种也很常见的方式,就是接SDK,比方说手游里常见的渠道服就是类似的玩法,我们做了一个游戏,接入A公司的SDK之后,需要登录的时候,调用SDK提供的接口,SDK会提供包括登录、验证身份证(就是防未成年的那个)等等各种功能。

emmm,总之就是,通常情况下,登录这个功能是不怎么需要我们游戏客户端开发人员去操心的。本文介绍的实际上是一种不常见的方法:把用户的登录功能直接写在Unity游戏本体中。(奇怪并且没啥用的知识增加了)

这种方法可能适合以下情况:

  1. 利用Unity制作非游戏类App,并且通常不方便接入外部SDK时.
  2. 缺乏原生开发人员的小公司或独立游戏团队,
  3. 爱瞎折腾的技术爱好者

那么,就开始吧。首先我们得稍微了解一下OpenID Connect是个啥玩意。


OpenID Connect

OpenID Connect这个词对于不少游戏客户端业务的程序员来说,可能是有点陌生的。同样陌生的,应该还有另一个词:OAuth 2.0.

OAuth 2.0

其实说来话长,这是对于很多刚入门互联网行业的同学老生常谈但是又很头疼的一个东西,光OAuth 2.0是个啥玩意可能就得出一本书才能讲完。

对于写给Unity开发者看的文章我们也不可能搞很大篇幅去讲解它的吧,我们就直接长话短说概括一下好了:OAuth 2.0是个常见的、被广泛使用的用于“授权”的互联网标准协议。我们通常见到的各种网站、App上的“使用QQ号登录”、“使用Google账号登录”、“使用GitHub账号”登录,等等功能,基本都是建立在OAuth 2.0 这个协议的基础上的.

统一单点登录(SSO)

说到用户登录,有的同学很聪明:这有啥难的,服务器给数据库加个表,记录一下用户名和密码,客户端登录的时候把用户名和密码传给服务器验证一下不就完了。

其实说的没错,道理确实就是这么个道理。而且学校里如果老师教过Java、PHP或者Asp的话,老师也确实就是这么教的。

但是问题就来了,比方说我们做了个游戏A,游戏A的服务器记录了一套用户名和密码,游戏发布了,没问题。然后接着我们又做了第二个游戏B,游戏B的服务器也记录了一套用户名和密码。

这时候玩家就懵了:没有这样的啊,你们同一家出的两个游戏,咋账号还不通用呢?我还得在两个游戏分别注册一个账号?不能够啊。

现实中也确实是这样,你看你玩CF和DNF,用的是同一个QQ号吧,你再登录个QQ音乐,用的也还是同一个QQ号吧。

那要怎么实现这个功能呢,方法很多,大家稍微开动脑筋就能想出一大堆方法。但其中最有理有据、久经验证可行的最佳方法自然就是:把管理账号的功能从各个游戏、应用里独立出来,单独作为一个系统而存在。

这就是我们所说的,统一单点登录系统。

而怎么实施统一单点登录呢,聪明的小伙伴们应该已经想到了,我们上面刚刚说的有一个“行业标准”(http://www.rfcreader.com/#rfc6749),叫OAuth 2.0.

角色与流程

在RFC6749的官方页面里,有这样一个描述

当然,看看就得了,估计一开始大概率也看不懂,我们简单解释一下。

以一个网络游戏为例,在整个登录过程中可以分为如下几个角色:

  1. User:也就是玩家本人(资源的拥有者)
  2. Client:游戏的客户端(代理“资源的拥有者(玩家)”行使相关行动的代理者)
  3. API:就是指我们游戏的服务器,至于它为啥叫API我们暂时不用管。(资源的提供者(资源服务器))
  4. 授权服务器:一个管理着用户信息的,同时提供OAuth2.0协议授权的服务。我们上文说统一单点登录时候提到的把账户功能单独提出来作为一个系统存在的,就是指授权服务器。

从这几个角色的描述,和图上的描述,虽然可能并没有看得懂,但是大概应该已经能猜到一件事了:在OAuth 2.0中,玩家、客户端、服务器、授权服务器之间是相互不信任的。

那么它具体的授权流程,大概就是这样的:

  1. 客户端首先向玩家请求授权
  2. 玩家在授权服务器登录账号,并同意向客户端授权。通常我们看到的这一步骤,最常见的做法就是客户端弹出一个网页跳转到授权服务器。

随便拿个游戏举例子

当我们点击登录之后,游戏客户端(其实这是启动器,但原理是一样的,我们就把它当成游戏本身就行)弹出了一个web浏览器窗口,在其中访问了该公司的“账号中心”,也就是我们说的授权服务器。并且从它的网页代码里,我们其实也大概能看出,它就是OAuth2.0实现的

这儿有个我们看不出来的小细节要说一下,我们的客户端打开了授权服务器的网页的同时,也告诉了授权服务器:“我需要这个授权是用来访问xxx这几个资源的”,于是有时候我们首次登录某个应用的时候,会看到类似这样的东西

这其中,“知乎 申请使用”中的“知乎”,就是我们这里所指代的Client,(OAuth2中的Client是广义的Client,并不一定是用户本地的才叫客户端,一个网站本身以Client身份来请求授权的话,它本身也是Client)。

“你的账号信息(昵称、头像、地区和性别)”这个就是Client告诉授权服务器,客户端希望能够访问的资源的范围。在我们的游戏客户端的案例中,这个范围自然就是指我们的游戏服务器了。

步骤还没完,我们继续往下看:

3. 玩家登录并同意给客户端授权之后,授权服务器会给客户端签发一个证书(token)(一种常见的证书形式叫jwt ,咱知道个名字就行了,有兴趣的同学可以自行扩展阅读:https://jwt.io/(反正够你学好久的))

4. 然后,我们的游戏客户端就算是获得了用户的授权了,它可以兴高采烈的拿着这个证书去找我们的游戏服务器了。那么问题来了,咱游戏服务器看到有个客户端拿着一份文件来说,“我要某某用户的资料,是他本人授权让我来的,你看这是他的授权信息”。我们游戏服务器也很懵啊,我怎么知道你这个证书是真的假的啊,万一你是个假的客户端,拿个假的证书来骗我,那到时候真正的用户不得咬死我啊。所以其实还没完,还有下一步。

5. 游戏服务器得拿着这个证书,去找我们的授权服务器:“有人说这是某某用户在你这儿签发的证书,你给看看是真的假的,是不是真是你这儿签发的”。授权服务器说,嗯是的,这玩意确实是我这儿签发的。游戏服务器说好的,我信了。于是到此为止,整个授权流程完成。

那么还有个问题,我们的授权服务器是怎么知道游戏服务器拿过来要求确认真假的证书(token)到底是不是从自己手里签发出去的呢。其实也很简单,以jwt为例(jwt是token的一种形式),jwt中有一小段是加密的,这个密码只有授权服务器它自己知道,所以只要验证这段加密的部分,与自己的密码是否一致即可。jwt中使用的常见加密方式是sha256,当然理论上你自己想怎么加密都行,反正只要到时候你自己还能认识它就行。


欸,那么说了半天,我们是不是,似乎好像跑题了?标题是OpenID Connect啊,我们这从sso讲到oauth的,闹了半天好像也没OpenID Connect什么事啊。别急,铺垫了半天,接下来就得说这个OpenID Connect了

OpenID Connect

OpenID Connect简称OIDC,它和我们上面说了半天的OAuth2.0是有很密切的关系的。如果愿意深入了解的话,我们可以在官网学到详细的内容(https://openid.net/connect/

我们刚刚说了半天OAuth,其中的核心是啥来着,是“授权” (Authorization)。怕忘了,请再跟我读一遍:Authorization.

然后我们的OIDC,是建立在OAuth 2.0这套协议基础之上的协议,它负责身份验证(Authentication)

Authentication和Authorization这两个词看着好像,意思也好像,缩写还都是一样的,一不小心就给搞晕了吧。所以赶紧多看几遍,然后我们再往下聊。

OIDC是基于OAuth2.0的协议,这意味着,我们上面花了巨大篇幅介绍的OAuth2.0的角色和授权流程,与OIDC基本是完全一致的。那么这一个是授权,一个是身份验证,它们有啥区别呢。

简单的说,“身份验证”负责解决“我是谁”的问题,而“授权”解决的是“我能干什么”的问题。

在只有OAuth2.0的情况下,“我是谁”和“我能干什么”是混在一起的,比如说上面讲的,“知乎 申请使用 你的账号信息(昵称、头像、地区和性别)”这个例子中,知乎是申请了在 【某个服务器中获取用户的账号信息】的权限。

而在OIDC协议里,通常OIDC的授权服务器自身就负责保存和提供关于用户的信息。


到此为止,我们需要用到的一些理论知识就都说完了。接下来就是实际建个工程来写代码,实现这一切了。

为了控制文章的篇幅不至于太大,我们把剩下的部分放在下一篇来说吧。

yomunsam

文章作者信息...

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

推荐ヾ(•ω•`)o