OAuth2.0: 接入GitHub登陆功能

OAuth

网络上关于Oauth 2.0协议的基本内容已经不少了,我就不重复写博客了,对基本概念不理解的同窗能够先自行Google。可是我发现实际演示的demo不多,因此写了这个偏实战的博客。css

  • 本文是以GitHub登陆为例来演示的。
  • 虽然线上环境确定要有云服务器,可是能够在本地直接模拟调试的
  • 不须要写一行代码就能够演示一个完成的登陆流程!!请读者务必手动的实际操做!!

其实OAuth认证说白了:html

  • 有三个角色:

    GitHub,用户,第三方网站third-sidenode

  • 须要就是完成一件事:

    经用户赞成,让third-side安全的从GitHub拿到一个tokengit

  • third-side拿到这个token能够用来github

    • 标识一个用户,读取用户的基本信息
    • 表明用户在GitHub上完成一些操做,好比写一个issue之类的

基本流程

  1. 向GitHub申请注册一个application

    一个application对应一个项目,咱们须要拿到一个client id 和 secret来用于后续的登陆认证数据库

  2. 构造相关的登陆连接,引导用户点击登陆。这一步须要用到上面的client id
  3. 用户赞成登陆后,third-side能够拿到一个code(后面详细解释),经过这个code能够向GitHub拿到用户的token

第一步注册App

由于咱们要使用GitHub做为第三方登陆,因此确定要先到官网上注册一个application。express

点击绿色的按钮就好了,点击后会出现👇的页面:浏览器

上面的内容不少,可是只有两个字段是关键的安全

  • Application Name: 这个是GitHub 用来标识咱们的APP的
  • Authorization callback url:就是上面我特地用红色字体标识的的,很关键
  • Homepage url 这个是展现用的,在咱们接下来的登陆中用不到,随便写就好了

这个Authorization callback url就是在用户确认登陆后,GitHub会经过这个url来告知咱们的服务器。因此真实状况下这个url应该是由专门的服务器程序来监听的。可是这一步你们能够先跟着我填写,后面细细说。服务器

完成注册后,咱们就有了这些数据:

上面有两个数据很关键

  • cliendt_id这是GitHub用来标识咱们的APP的

    接下来咱们须要经过这个字段来构建咱们的登陆url

  • client Secret 这个很关键,等会咱们就靠它来认证的,要好好保存。我这个只是演示教程,用完就销毁了,因此直接公开了。

构造URL

而后咱们接下来怎么作?很简单

<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请求和响应

而后用户点击确认后发生了什么?

  • 用户点击确认,就是向GitHub发送一个报文,确认本身确实到登陆某网站
  • GitHub收到这个用户的确认消息,而后会返回一个状态码为301的报文

这个是第二次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,须要

  • code

    这个只有在用户赞成登陆后,服务器才能拿到。

  • client secret + client id
  • https://github.com/login/oauth/access_token发起POST请求,而且携带上面的三个字段

    这个url是GitHub规定的,你能够在它的官方文档中找到

而后咱们在postman中构造这样的请求:

哎,能够看到,这咱们确实拿到了用户的token:

access_token=9094eb58a23093fd593d43eb28c1f06ce7904ed5&scope=&token_type=bearer

只不过真实状况下上面的操做都是由线上的服务器完成的,我这样操做是方便你们的理解。

代码实战

经过上面的例子咱们能够看到,若是只是演示,咱们是不须要服务器。接下来咱们在本地用代码直接演示下。

  • 演示的代码使用node + express + request 写的
  • 不过代码很简单,不了解上面的技术栈也能够看得懂
  • 流程上和以前演示的如出一辙,只是经过代码来完成
  • 源代码看这里 GitHub

由于咱们要启动本地的服务器来监听响应,因此咱们首先要修改下咱们的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
  • 收到用户浏览器传递的code以后,咱们的服务器向GitHub申请了用户token
  • 将token返回给用户,代表登陆成功

不过其实我上面省略了不少操做。正常状况下,服务器拿到用户的token后,应该:

  • 把token保存到数据库
  • 经过token从github拿到用户的名称之类的数据
  • 不该该把token返回给用户
  • 返回给用户一个cookie,使得用户保持登陆,而且在数据库中把这个cookie和用户的token对应起来

总结

好了,上面基本就是一次登陆流程。

  • 构造登陆连接,引导用户登陆
  • 用户登陆后,GitHub会把用户重定向到咱们预先设置好的URL,同时携带一个code
  • 服务器拿到这个code,向GitHub申请token
  • 拿到token后,服务器就能够确认用户成功登陆了,而后能够返回一个登陆成功的页面
相关文章
相关标签/搜索