基于 Oauth2.0 的 GitHub、Google、Facebook、Weibo 等平台第三方用户登录认证
编程 |

        现在开发第三方平台登录功能应该不是什么难事,都是 oauth2.0 协议,按照文档实现一遍应该就可以了。Github 开发最方便,注册完成之后不需要审核,而且 redirect_url 可以设置成本地地址;Facebook 开发也比较方便,这里需要表扬一下,Facebook的中文文档很全面,Graph api(官方翻译:图谱 API)很好用,还能在线调试。谷歌的开发文档就差了很多,没有中文而且会造成误导。现在Facebook 和 Google 的应用发布之前都是要审核的,而且应用需要有明确可访问的隐私政策页面,这点就很麻烦。相比之下新浪微博的审核基本上域名备案之后就容易了。

注册

        各家基本相同:

  1. Github 在 开发者应用 注册一个应用就能拿到 Client IDClient Secret,设置好名称、网址、回调地址基本上就完成。具体内容可以参考Authorizing OAuth Apps

    图片说明
  2. Google 在 开发者控制台 新建一个项目,然后选择创建一个凭据,注意选择 OAuth 类型的凭据,这个是用来识别用户的,然后选择网页类型的应用。创建凭据的时候需要设置 redirect_url。然后设置 “ OAuth 同意屏幕”,包括名称、图标、域名、首页链接,和“应用隐私权政策链接 ”。这个链接必须是可访问的,否则 Google 会通知审核失败。域名也需要验证所有权,这个可以选择 Google 分析认证,也可以添加代码认证。

    谷歌创建项目 谷歌创建凭据
  3. Facebook 在开发者中心点击创建应用,然后输入名称。在 “控制面板” -> “设置” -> “基本” 菜单下设置名称、邮箱、域名、隐私政策地址、回调地址等。这里甚至需要填写 数据安全官联系方式,简直可怕。Facebook 的应用需要在产品里面添加 “ Facebook 登录” 才能使用。

    facebook 项目信息
  4. 新浪微博 在 微博开放平台 创建 web 应用,进入应用信息填写名称、网址、图标等信息,提交审核可以了。微博需要上传三个不同大小的图标和三个300px · 450px 的图片,大小必须完全符合规定。

    微博信息

开发

        Google、Facebook、新浪微博 都提供了 js sdk,可以用来直接生成登录按钮。这种方式更加社交化,其中 Google 会放弃这种方式(可能因为 Google+ 项目失败了)。这里只介绍基于 Oauth 的认证。

一、生成登录按钮

        最简单的方式就是使用 button 标签,加个 a 标签完工,另外各家平台也有自己的素材库可以使用。不过前者不能体现平台的差异,后者风格各异,不美观。这里推荐使用 svg + css 方式生成按钮,可自定义程度较高。 有很多开放无版权的各大平台 LOGO svg 素材。比如下面这种,我是参考 stack overflow 的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .fb-btn:hover {
            background-color: #3367d6 !important;
        }
    </style>
</head>
<body>
<a href="https://www.facebook.com/v3.2/dialog/oauth?client_id=123&redirect_uri=https://example.com/callback&state=1234" style="text-decoration: none;" rel="nofollow">
    <div class="fb-btn" style=" width: 280px;color:#FFFFFF;text-align: center;border-radius: 5px;background-color: #4285f4;">
        <svg width="18px" height="18px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216 216" class="_5h0m" color="#FFFFFF">
            <path fill="#FFFFFF" d="
          M204.1 0H11.9C5.3 0 0 5.3 0 11.9v192.2c0 6.6 5.3 11.9 11.9
          11.9h103.5v-83.6H87.2V99.8h28.1v-24c0-27.9 17-43.1 41.9-43.1
          11.9 0 22.2.9 25.2 1.3v29.2h-17.3c-13.5 0-16.2 6.4-16.2
          15.9v20.8h32.3l-4.2 32.6h-28V216h55c6.6 0 11.9-5.3
          11.9-11.9V11.9C216 5.3 210.7 0 204.1 0z"></path>
        </svg>
        <span class="text-inline" style="font-size: 16px;line-height: 48px; ">Sign in with Facebook</span>
    </div>
</a>
</body>
</html>

        按钮的链接需要根据各个平台的要求来设置,一般根据 OAuth 协议这里需要设置 client_id、redirect_uri、state(可选)、scope(申请的权限,可选)。当用户同意授权之后,用户的浏览器会对 redirect_uri 发起一个 get 请求,同时会携带 code 和 state(如果设置了回调的时候会带上) 参数。code 用来在服务器端请求token。

        注意这里的state,这个虽然是可选的,但是对于服务器安全是很重要的。对于参数 state 微博接口是这么描述的:

用于保持请求和回调的状态,在回调时,会在Query Parameter中回传该参数。开发者可以用这个参数验证请求有效性,也可以记录用户请求授权页前的位置。这个参数可用于防止跨站请求伪造(CSRF)攻击

        Google 是这么描述的:

可选描述
(Optional, but strongly recommended)An opaque string that is round-tripped in the protocol; that is to say, it is returned as a URI parameter in the Basic flow, and in the URI #fragment in the Implicit flow. The state can be useful for correlating requests and responses. Because your redirect_uri can be guessed, using a state value can increase your assurance that an incoming connection is the result of an authentication request. If you generate a random string or encode the hash of some client state (e.g., a cookie) in this state variable, you can validate the response to additionally ensure that the request and response originated in the same browser. This provides protection against attacks such as cross-site request forgery

        说了一大堆,大致意思就是,由于回调地址是公开的,而且参数 code 是能够被构造出来的,这样就可能会有人恶意发送请求,使用 state 可以验证请求的有效性。在服务器流程介绍的第一步是这么描述的:

You must protect the security of your users by preventing request forgery attacks. The first step is creating a unique session token that holds state between your app and the user's client. You later match this unique session token with the authentication response returned by the Google OAuth Login service to verify that the user is making the request and not a malicious attacker. These tokens are often referred to as cross-site request forgery (CSRF) tokens.

        意思就是在生成 URL 的时候,需要同时生成 state 并且把 state 存储到 session 里,这样客户端回调的时候就会在 URL 里公开的拿到 state ,同时在 session 拿到加密的 state,如果 state 不存在或者不一致就表示无效请求。

但是,在实际开发中回调的请求 session 中没有 state 。这是因为 cookie 里有一个属性 SameSite,这个值一般默认为Strict,表示只能在请求的 url 和设置这个 cookie 的的 url 在同一个 domain 下面时才会带上这个 cookie,这是防止 CSRF 攻击的基础。但是因为这个回调请求是由平台发起的,违反了同源规则,所以在回调接口是看不到 session 的,这个需要做一次跳转,而且必须是由 js 发起的跳转,不能直接使用 30x 跳转。因为 30x 跳转 是符合链式规则的,也就是直接跳转浏览器还是不会携带 cookie 的。使用 js 跳转可以跳出这个来源限制,从而带上 cookie。

二、回调接口处理请求

         当用户点击同意之后,浏览器就会带上 code 参数跳转到设置好的回调地址。注意 Facebook 平台用户点击拒绝也会发起回调请求。这里的逻辑很简单。

  1. 验证请求有效性。
  2. 用拿到的 code, 外加 Client IDClient Secretredirect_url 一起发送 get 请求,获取 access_token

        理论上 OAuth 的逻辑就结束了,下面使用 token 获取个性信息的接口各有不同。一些网站还可以验证 token 的有效性,我没有使用,就不介绍了。

三、获取用户信息

        一般会在拿到 Token 的同时就会请求用户信息,必定用户希望看到登录的结果。这一步也要在服务器端发起请求,具体操作可以查看文档。

四、资源重定向

        到目前为止,浏览器的地址依然是回调地址,这个肯定不是用户需要的。这里就需要跳转到用户需要的网页。

总结

        一般都是 OAuth2.0 协议的基础上开发,各家平台略有不同。同时在 Facebook 隐私风波之后,各大平台都加紧了对应用的审核,上线的难度加大了。

附录:

  1. GitHub: https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/
  2. Google: https://developers.google.com/identity/protocols/OpenIDConnect
  3. Facebook: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow
  4. 微博: https://open.weibo.com/wiki/Connect/login