参考:
- 从密码到token, 一个授权的故事
- 理解OAuth 2.0
- 小程序官方文档
- 微信小程序之登录态维护(十一)
微信的登陆认证方式跟Oauth的授权码认证模式非常相似,接下来我大致讲解Oauth的三种常用模式以及与微信登陆认证的关联。
Oauth的三种常用模式
密码模式
密码模式的登陆方式大致如上,实际场景里,当用户在登陆掘金客户端的时候,可以选择github认证登陆,而不是直接账号密码登陆时。如果这个时候掘金客户端允许用户直接在上面输入账号密码,那么客户端在用户点击登陆时,将输入的信息转发至github授权服务器上请求授权登陆。如果github服务器授权通过,会返回成功登陆信息,同时我们也成功登陆客户端。
另外在上面整个过程里,掘金客户端不允许保存用户的密码。
对应的登陆请求如下:
//客户端直接带上账户名、密码访问授权服务器
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w
授权服务器返回的信息如下:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
这是第一种Oauth认证方式,这种认证方式其实是用户在客户端直接录入授权账号和密码,由客户端直接发起对授权服务器的登陆认证,其中客户端不允许保存密码。
相对应的,这种认证最大的缺点就是用户账号密码全部记录在客户端的前端界面,通过请求传输给授权服务器,整个过程里密码容易泄露,安全性差
简化模式
接着,我们来看第二种认证模式。相比上面那种直接在客户端输入账号密码的方式,我们转换下思路,在授权登陆的时候,客户端带上之前在授权服务器里注册过的AppId、AppSecret和登陆成功后的重定向URI,直接访问对应的授权服务器。授权服务器接收请求,转至登陆认证界面。
用户在授权服务器的认证中心下进行账号密码的录入,点击登陆时,授权服务器通过AppId和App_Secret来识别客户端,如果识别通过,将token通过hash fagment的形式附着在重定向地址上,返回给客户端。
在整个过程里,客户端都不接触用户名和密码,只需要保存授权服务器返回的token,在后续的API请求里带上token即可。
客户端发起的请求如下:
//客户端需要带上注册过的id、重定向地址,也可以带上对应的密钥
GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
授权服务器返回的信息:
//授权服务器的token通过#号隔开,直接附着在地址上
HTTP/1.1 302 Found
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
&state=xyz&token_type=example&expires_in=3600
这种方式最大的缺点是token通过重定向地址直接返回客户端,安全性差,容易被人通过浏览器的历史记录或者访问日志里窃取
授权码模式
基于第二种模式里,token是直接通过URL地址返回给客户端导致安全性差的问题,那么我们可以变通一下,在授权服务器重定向至客户端时,不要直接带上token,带上某个特殊的授权码code表示服务器已经同意授权认证。
接着让客户端的服务器后台发起请求,把客户端在授权服务器里注册过的AppId、AppSecret、接收的授权码code带上,由授权服务器通过Id和Secret密钥对code进行解密验证,如果验证通过表示当前请求的客户端是正确的,接着再返回实际的token给客户端即可。
客户端第一次发起的请求:
//客户端第一次发起请求,带上id和重定向地址
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
授权服务器返回的信息:
//授权服务器登陆认证通过,返回对应的授权码code
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
&state=xyz
客户端第二次发起的请求:
//客户端第二发起的请求,带上id、secret和重定向地址
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
授权服务器最终返回的信息:
//返回对应的token
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
授权码认证模式里授权服务器不直接返回token,而是返回授权码,由开发者服务器带上相关信息后台发送请求,授权服务器进行单独的授权认证之后才返回token。通过两次请求再返回token,保证了安全性。
微信认证方式
微信的登陆认证方式,其实跟授权码模式很相似,区别在于简化了获取授权码code的过程,不需要带上账号密码,调用wx.login就可以从微信服务器上返回授权码,并且整个过程不需要繁琐地重定向,通过后台请求就可以获取token。
这四种认证方式对应的关系如下图所示: