网络上关于Oauth 2.0协议的基本内容已经不少了,我就不重复写博客了,对基本概念不理解的同窗能够先自行Google。可是我发现实际演示的demo不多,因此写了这个偏实战的博客。css
其实OAuth认证说白了:html
GitHub,用户,第三方网站third-sidenode
经用户赞成,让third-side安全的从GitHub拿到一个tokengit
third-side拿到这个token能够用来github
一个application对应一个项目,咱们须要拿到一个client id 和 secret来用于后续的登陆认证数据库
由于咱们要使用GitHub做为第三方登陆,因此确定要先到官网上注册一个application。express
点击绿色的按钮就好了,点击后会出现👇的页面:浏览器
上面的内容不少,可是只有两个字段是关键的安全
Application Name
: 这个是GitHub 用来标识咱们的APP的Authorization callback url
:就是上面我特地用红色字体标识的的,很关键这个Authorization callback url
就是在用户确认登陆后,GitHub会经过这个url来告知咱们的服务器。因此真实状况下这个url应该是由专门的服务器程序来监听的。可是这一步你们能够先跟着我填写,后面细细说。服务器
完成注册后,咱们就有了这些数据:
上面有两个数据很关键
cliendt_id
这是GitHub用来标识咱们的APP的接下来咱们须要经过这个字段来构建咱们的登陆url
client Secret
这个很关键,等会咱们就靠它来认证的,要好好保存。我这个只是演示教程,用完就销毁了,因此直接公开了。而后咱们接下来怎么作?很简单
<body> <a href="https://github.com/login/oauth/authorize\ ?client_id=47878c9a96cfc358eb6e"> login with github</a> </body>
咱们只要在咱们的登陆页面添加这样的代码,引导用户点击咱们的登陆按钮就好了。
注意到上面的流程了吗? 其实咱们已经完成了一半的登陆认证流程了,而后咱们来分析一下。
首先请你们思考一下:从用户点击login with github
开始,中间有几回重要的HTTP报文传递?
不重要是指中间那些网页中附带的对css js资源的请求,这些不算。
与登陆认证有关的http请求有几个?
3个!!!说清楚这一点,基本就明白了。
首先咱们构造了这样的html页面
<body> <a href="https://github.com/login/oauth/authorize\ ?client_id=47878c9a96cfc358eb6e"> login with github</a> </body>
关键是咱们构造了一个url
https://github.com/login/oauth/authorize
:这一部分是固定的,只要是用GitHub登陆,就得这样。你能够从官方文档上看到这个连接。
若是是微信登陆,twitter登陆,也是大同小异,具体的url能够在相关的官网上找到。
GitHub会监听这个路由,来作出登陆处理
client_id=47878c9a96cfc358eb6e
。它的值就是咱们以前申请到的client id。这是个必须存在的字段,用户点击登陆后,GitHub就是经过这个字段来确认用户到底是想登陆哪一个网站。
而后GitHub会返回这样的页面,确认用户是否真的要登陆,这就是第一次HTTP请求和响应
而后用户点击确认后发生了什么?
这个是第二次HTTP请求和响应。由于浏览器收到301重定向后,会直接前往新的网址了,因此你要是没仔细看的话,可能就忽略了。
这个状态码为301的报文,它的Location字段大概长这样:https://github.com/fish56/OAuth?code=a1aee8cacf7560825665>
https://github.com/fish56/OAuth
这个字段就是咱们以前填写的callback url,正常状况下这个应该是咱们云服务器的网址。可是这里为了演示方便,我这里就随便填写了个人github地址,不要紧的
code=a1aee8cacf7560825665
:(由于笔者中间调试过,因此如今写的token和gif里面的不同哈)
这个就是咱们OAuth登陆的一个核心信息。我以前说过,咱们OAuth登陆的核心目的就是让第三方网站可以安全的拿到用户的token。用户的浏览器收到以前的301的HTTP响应后,就会向咱们服务器发起请求,请求的同时服务器就拿到了这个code。服务器就能够经过这个code从github拿到用户的token。
这就是第三次http请求。
而后又有同窗可能会问了,为何要返回一个code,而不是直接返回一个token呢?
答:为了安全,若是token通过用户的手里走一遍,就可能会被其余恶意的人窃取。
OAuth协议下,GitHub会返回给用户一个code,而后用户浏览器经过重定向携带这个code来访问咱们的服务器,这样服务器就拿到了这个code。
服务器拿到这个code 以后,经过结合以前的client secret向GitHub申请token,这样会安全一点。
以前咱们只作了一半, 接下来咱们经过postman来演示一下如何经过code拿到token。
咱们要向GitHub申请用户的token,须要
这个只有在用户赞成登陆后,服务器才能拿到。
https://github.com/login/oauth/access_token
发起POST请求,而且携带上面的三个字段这个url是GitHub规定的,你能够在它的官方文档中找到
而后咱们在postman中构造这样的请求:
哎,能够看到,这咱们确实拿到了用户的token:
access_token=9094eb58a23093fd593d43eb28c1f06ce7904ed5&scope=&token_type=bearer
。
只不过真实状况下上面的操做都是由线上的服务器完成的,我这样操做是方便你们的理解。
经过上面的例子咱们能够看到,若是只是演示,咱们是不须要服务器。接下来咱们在本地用代码直接演示下。
由于咱们要启动本地的服务器来监听响应,因此咱们首先要修改下咱们的callback URL。
请你们将这个callback URL自行修改成http://localhost:8099/github/login
。
这是咱们程序的目录结构
而后这是咱们的node代码:
const querystring = require('querystring'); const express = require('express'); const request = require('request'); const githubConfig = require('./oauth.conf') let app = express(); // 作一个路由函数,监听/github/login 的get请求 app.get('/github/login', async function(req,res){ //read code from url let code = req.query.code // 收到code后,向GitHub请求用户的token request.post(githubConfig.access_token_url, { form:{ client_id: githubConfig.client_ID, client_secret: githubConfig.client_Secret, code: code } },function(error, response, body) { //正常状况下,返回值应该是形如access_token=9094eb58a23093fd59 // 3d43eb28c1f06ce7904ed5&scope=&token_type=bearer // 的字符串,能够经过下面的函数来解析 let result = querystring.parse(body) // 拿到token后,返回结果,表示咱们成功了 let access_token = result["access_token"] if(access_token == undefined){ res.send(result.error_description) } res.send(`You are login! you token is ${access_token}`) }) }) // 监听 8999 ,启动程序。注意端口号要和咱们以前填写的保持一致 app.listen(8099,function(){ console.log('listening localhost:8099') })
这是oauth.conf.js
。注意把相关的字段换成你本身的配置。
module.exports = { client_ID: '47878c9a96cfc358eb6e', client_Secret: '4813689c043c60dbf3d3a0d8e0a984afc0bf810a', access_token_url: 'https://github.com/login/oauth/access_token' }
这是实际效果
咱们的代码作了什么事?只是把咱们以前的手动使用postman作的事情自动化了
http://localhost:8099/github/login
不过其实我上面省略了不少操做。正常状况下,服务器拿到用户的token后,应该:
好了,上面基本就是一次登陆流程。