很多的网站、APP 都弱化了甚至没有搭建属于自己的账号体系,而是使用其它社会化的第三方登陆的方式,比如在登陆某个网站的时候选择通过 github 或者微信、微博等方式登陆,这样不仅免去了用户注册账号的麻烦,还可以获取用户的好友关系来增强自身的社交功能。
例如要通过 github 这样的第三方网站去登陆某个没有自己账号体系的平台,最传统的方式是直接在该平台的登陆页面输入 github 的账号密码,该平台通过用户的账号和密码去 github 上面获取用户的数据,但是这样做有很多缺陷:
- 该平台需要明文保存用户的 github 账号和密码,不安全;
- 该平台拥有获取用户在 github 的所有权限;
- 用户只有修改密码,才能收回赋予该平台的权限,会导致其它获得用户授权的第三方应用全部失效;
- 只要有一个第三方应用程序被破解,就会导致用户密码泄露,以及所有使用 github 登陆过的网站的数据泄漏;
为了解决以上问题,就有了 OAuth。
原理
OAuth在“客户端”和“服务端”之间设置了一个授权层(authorization layer)。“客户端”不能直接登陆“服务端”,只能登陆授权层,以此将用户与客户端区分开来。“客户端”登陆授权层所用的token与用户的密码不同,用户可以在登陆的时候,指定授权层 token 的权限范围和有效期。 “客户端”登陆授权层之后,“服务端”根据 token 的权限范围和有效期,向“客户端”开放用户存储的资料。
例如现在有一个平台为平台 A,平台 A 的登陆方式只有通过谷歌账号第三方登录机制登录:
- 用户点击 Sign in with Google 跳转到授权页面,授权页面的 URL 中主要包含的参数有:
- client_id: 在 Google 中申请应用 ID;
- redirect_uri: 授权成功之后要跳转到的地址;
- 页面自动跳转到初始参数中
redirect_uri
定义的 URL,并自动在URL末尾添加一个code
参数 - 平台 A 通过上一步获取的
code
参数换取 Token,平台 A 使用POST https://oauth2.googleapis.com/token
获取 Token,需要包含以下参数:- client_id: 在 Google 申请的应用 ID;
- client_secret: 在 Google 申请时提供的 APP Secret;
- grant_type: 需要填写
authorization_code
; - code: 上一步获得的
code
; - redirect_uri: 回调地址,需要与注册应用里的回调地址以及第一步的
redirect_uri
参数
- 通过第三步的请求,接口返回Token和相关数据
{ "access_token": "ACCESS_TOKEN", // Token的值 "expires_in": 1000, // 过期时间 "uid": "1234567", // 当前授权用户的UID }
- 使用在第四步中获取到的access_token,就可以去获取用户的资源了
- Google返回用户信息,平台A进行处理,整个流程结束
通过以上方式,在平台A和Google之间建立了一个独立的权限层,这个权限由用户赋予,可以被用户随时取消,不同于第三方应用之间的相互独立,互不干扰,这样就解决了明文存放账号密码的问题。
Access Token
Access Token 是用于访问被保护的资源的一种凭据,它是一个不透明的字符串。一般 Access Token 的有效时间都比较短,如果想要用户不用频繁登录平台 A,就需要用到 Refresh Token。至于为什么需要用到 Refresh Token 而不是再次去获得 Access Token,主要是因为获取 Access Token 时需要使用到一个 code,而生成这个 code 需要用户再次进行操作,而有了 Refresh Token,用户就可以不用再次进行操作。
Refresh Token
在 OAuth 机制中,Refresh Token 并不是必须设置的,但是不设置 Refresh Token,则会增加用户登录的次数。Refresh token 的作用是刷新 Access token。为 Refresh Token 是保存在客户端的服务器上的,当前的 Access Token 失效或者过期时,Refresh Token 就会去获取一个新的 Token,Refresh Token 也是一个对客户端不透明的字符串。
一个比较有效的token返回结果如下:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
"scope": "create"
}
大致流程如下:
- 客户端通过认证服务器请求认证;
- 认证服务器检验客户端认证是否有效,如果有效,返回一个 Access Token 和一个 Refresh Token;
- 客户端通过 Access Token 去请求服务器的资源;
- 如果 Access Token 有效,服务器返回给客户端资源,如果 Access Token 失效,服务器返回给客户端 Token 失效的信息,然后客户端会通过 Refresh Token 再次请求获取新的 Access Token;
Refresh Token 本身也是有过期时间的,一般会比 Access Token的过期时间长很多,如果想要将 Refresh Token 设置为永久有效,则可以通过配置参数实现。