OAuth认证流程
背景
很多的网站、APP都弱化了甚至没有搭建属于自己的账号体系,而是使用其它社会化的第三方登陆的方式,比如在登陆某个网站的时候选择通过github或者微信、微博等方式登陆,这样不仅免去了用户注册账号的麻烦,还可以获取用户的好友关系来增强自身的社交功能。
例如要通过github这样的第三方网站去登陆某个没有自己账号体系的平台,最传统的方式是直接在该平台的登陆页面输入github的账号密码,该平台通过用户的账号和密码去github上面获取用户的数据,但是这样做有很多缺陷:
- 该平台需要明文保存用户的github账号和密码,不安全;
- 该平台拥有获取用户在github的所有权限;
- 用户只有修改密码,才能收回赋予该平台的权限,会导致其它获得用户授权的第三方应用全部失效;
- 只要有一个第三方应用程序被破解,就会导致用户密码泄露,以及所有使用github登陆过的网站的数据泄漏;
为了解决以上问题,就有了OAuth。
原理
OAuth在“客户端”和“服务端”之间设置了一个授权层(authorization layer)。“客户端”不能直接登陆“服务端”,只能登陆授权层,以此将用户与客户端区分开来。“客户端”登陆授权层所用的oken与用户的密码不同,用户可以在登陆的时候,指定授权层token的权限范围和有效期。
“客户端”登陆授权层之后,“服务端”根据token的权限范围和有效期,向“客户端”开放用户存储的资料。
例如现在有一个平台为平台A,平台A的登陆方式只有通过谷歌账号第三方登录机制登录。流程图大致如下:
- 用户点击Sign in with Google
跳转到授权页面,授权页面的URL中主要包含的参数有:
- client_id: 在Google中申请应用ID;
- redirect_uri: 授权成功之后要跳转到的地址;
- 页面自动跳转到初始参数中redirect_uri定义的URL,并自动在URL末尾添加一个
code
参数 - 平台A通过上一步获取的code参数换取Token,平台A请求如下接口获取Token
POST https://oauth2.googleapis.com/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设置为永久有效,则可以通过配置参数实现。
参考链接:
http://www.rfcreader.com/#rfc6749
https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/