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後,服務器就能夠確認用戶成功登陸了,而後能夠返回一個登陸成功的頁面
相關文章
相關標籤/搜索