github 授權登錄教程與如何設計第三方授權登錄的用戶表

效果圖

需求:在網站上想評論一篇文章,而評論文章是要用戶註冊與登錄的,那麼怎麼免去這麻煩的步驟呢?答案是通過第三方授權登錄。本文講解的就是 github 授權登錄的教程。

效果體驗地址: http://biaochenxuying.cn

1. github 第三方授權登錄教程

先來看下 github 授權的完整流程圖 1:

github 1

或者看下 github 授權的完整流程圖 2:

github 2

1.1 申請一個 OAuth App

首先我們必須登錄上 github 申請一個 OAuth App,步驟如下:

  1. 登錄 github
  2. 點擊頭像下的 Settings -> Developer settings 右側 New OAuth App
  3. 填寫申請 app 的相關配置,重點配置項有2個
  4. Homepage URL 這是後續需要使用授權的 URL ,你可以理解爲就是你的項目根目錄地址
  5. Authorization callback URL 授權成功後的回調地址,這個至關重要,這是拿到授權 code 時給你的回調地址。

具體實踐如下:

    1. 首先登錄你的 GitHub 賬號,然後點擊進入Settings。

    1. 點擊 OAuth Apps , Register a new application 或者 New OAuth App 。

    1. 輸入信息。

image.png

    1. 應用信息說明。

流程也可看 GitHub 設置的官方文檔-Registering OAuth Apps

1.2 授權登錄

github 文檔:building-oauth-apps/authorizing-oauth-apps

授權登錄的主要 3 個步驟:

筆者這次實踐中,項目是採用前後端分離的,所以第 1 步在前端實現,而第 2 步和第 3 步是在後端實現的,因爲第 2 個接口裏面需要Client_secret 這個參數,而且第 3 步獲取的用戶信息在後端保存到數據庫。

1.3. 代碼實現

1.3.1 前端

筆者項目的技術是 react。

// config.js

// ***** 處請填寫你申請的 OAuth App 的真實內容
 const config = {
  'oauth_uri': 'https://github.com/login/oauth/authorize',
  'redirect_uri': 'http://biaochenxuying.cn/',
  'client_id': '*****',
  'client_secret': '*******',
};

// 本地開發環境下
if (process.env.NODE_ENV === 'development') {
  config.redirect_uri = "http://localhost:3001/"
  config.client_id = "******"
  config.client_secret = "*****"
}
export default config; 

代碼參考 config.js

redirect_uri 回調地址是分環境的,所以我是新建了兩個 OAuth App 的,一個用於線上生產環境,一個用於本地開發環境。

一般來說,登錄的頁面應該是獨立的,對應相應的路由 /login , 但是本項目的登錄 login 組件是 nav 組件的子組件,nav 是個全局用的組件, 所以回調地址就寫了 http://biaochenxuying.cn/

  • 所以點擊跳轉是寫在 login.js 裏面;
  • 授權完拿到 code 後,是寫在 nav.js 裏面
  • nav.js 拿到 code 值後去請求後端接口,後端接口返回用戶信息。
  • 其中後端拿到 code 還要去 github 取 access_token ,再根據 access_token 去取 github 取用戶的信息。
// login.js

// html
<Button
    style={{ width: '100%' }}
    onClick={this.handleOAuth} >
      github 授權登錄
</Button>

// js
handleOAuth(){
    // 保存授權前的頁面鏈接
    window.localStorage.preventHref = window.location.href
    // window.location.href = 'https://github.com/login/oauth/authorize?client_id=***&redirect_uri=http://biaochenxuying.cn/'
    window.location.href = `${config.oauth_uri}?client_id=${config.client_id}&redirect_uri=${config.redirect_uri}`
}

代碼參考 login.js

// nav.js

componentDidMount() {
    // console.log('code :', getQueryStringByName('code'));
    const code = getQueryStringByName('code')
    if (code) {
      this.setState(
        {
          code
        },
        () => {
          if (!this.state.code) {
            return;
          }
          this.getUser(this.state.code);
        },
      );
    }
  }

componentWillReceiveProps(nextProps) {
    const code = getQueryStringByName('code')
    if (code) {
      this.setState(
        {
          code
        },
        () => {
          if (!this.state.code) {
            return;
          }
          this.getUser(this.state.code);
        },
      );
    }
  }
  getUser(code) {
    https
      .post(
        urls.getUser,
        {
          code,
        },
        { withCredentials: true },
      )
      .then(res => {
        // console.log('res :', res.data);
        if (res.status === 200 && res.data.code === 0) {
          this.props.loginSuccess(res.data);
          let userInfo = {
            _id: res.data.data._id,
            name: res.data.data.name,
          };
          window.sessionStorage.userInfo = JSON.stringify(userInfo);
          message.success(res.data.message, 1);
          this.handleLoginCancel();
          // 跳轉到之前授權前的頁面
          const href = window.localStorage.preventHref
          if(href){
            window.location.href = href 
          }
        } else {
          this.props.loginFailure(res.data.message);
          message.error(res.data.message, 1);
        }
      })
      .catch(err => {
        console.log(err);
      });
  }

參考 nav.js

1.3.2 後端

筆者項目的後端採用的技術是 node.js 和 express。

  • 後端拿到前端傳來的 code 後,還要去 github 取 access_token ,再根據 access_token 去取 github 取用戶的信息。
  • 然後把要用到的用戶信息通過 註冊 的方式保存到數據庫,然後返回用戶信息給前端。
// app.config.js

exports.GITHUB = {
    oauth_uri: 'https://github.com/login/oauth/authorize',
    access_token_url: 'https://github.com/login/oauth/access_token',
    // 獲取 github 用戶信息 url // eg: https://api.github.com/user?access_token=******&scope=&token_type=bearer
    user_url: 'https://api.github.com/user',

    // 生產環境
    redirect_uri: 'http://biaochenxuying.cn/',
    client_id: '*****',
    client_secret: '*****',

    // // 開發環境
    // redirect_uri: "http://localhost:3001/",
    // client_id: "*****",
    // client_secret: "*****",
};

代碼參考 app.config.js

// 路由文件  user.js

const fetch = require('node-fetch');
const CONFIG = require('../app.config.js');
const User = require('../models/user');

// 第三方授權登錄的用戶信息
exports.getUser = (req, res) => {
  let { code } = req.body;
  if (!code) {
    responseClient(res, 400, 2, 'code 缺失');
    return;
  }
  let path = CONFIG.GITHUB.access_token_url;
  const params = {
    client_id: CONFIG.GITHUB.client_id,
    client_secret: CONFIG.GITHUB.client_secret,
    code: code,
  };
  // console.log(code);
  fetch(path, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json', 
    },
    body: JSON.stringify(params),
  })
    .then(res1 => {
      return res1.text();
    })
    .then(body => {
      const args = body.split('&');
      let arg = args[0].split('=');
      const access_token = arg[1];
      // console.log("body:",body);
      console.log('access_token:', access_token);
      return access_token;
    })
    .then(async token => {
      const url = CONFIG.GITHUB.user_url + '?access_token=' + token;
      console.log('url:', url);
      await fetch(url)
        .then(res2 => {
          console.log('res2 :', res2);
          return res2.json();
        })
        .then(response => {
          console.log('response ', response);
          if (response.id) {
            //驗證用戶是否已經在數據庫中
            User.findOne({ github_id: response.id })
              .then(userInfo => {
                // console.log('userInfo :', userInfo);
                if (userInfo) {
                  //登錄成功後設置session
                  req.session.userInfo = userInfo;
                  responseClient(res, 200, 0, '授權登錄成功', userInfo);
                } else {
                  let obj = {
                    github_id: response.id,
                    email: response.email,
                    password: response.login,
                    type: 2,
                    avatar: response.avatar_url,
                    name: response.login,
                    location: response.location,
                  };
                  //註冊到數據庫
                  let user = new User(obj);
                  user.save().then(data => {
                    // console.log('data :', data);
                    req.session.userInfo = data;
                    responseClient(res, 200, 0, '授權登錄成功', data);
                  });
                }
              })
              .catch(err => {
                responseClient(res);
                return;
              });
          } else {
            responseClient(res, 400, 1, '授權登錄失敗', response);
          }
        });
    })
    .catch(e => {
      console.log('e:', e);
    });
};

代碼參考 user.js

至於拿到 github 的用戶信息後,是註冊到 user 表,還是保存到另外一張 oauth 映射表,這個得看自己項目的情況。

從 github 拿到的用戶信息如下圖:

github-login.png

最終效果:

github-logining.gif

參與文章:

  1. https://www.jianshu.com/p/a9c...
  2. https://blog.csdn.net/zhuming...

2. 如何設計第三方授權登錄的用戶表

第三方授權登錄的時候,第三方的用戶信息是存數據庫原有的 user 表還是新建一張表呢 ?

答案:這得看具體項目了,做法多種,請看下文。

第三方授權登錄之後,第三方用戶信息一般都會返回用戶唯一的標誌 openid 或者 unionid 或者 id,具體是什麼得看第三方,比如 github 的是 id

  • 1. 直接通過 註冊 的方式保存到數據庫

第一種:如果網站 沒有 註冊功能的,直接通過第三方授權登錄,授權成功之後,可以直接把第三的用戶信息 註冊 保存到自己數據庫的 user 表裏面。典型的例子就是 微信公衆號的授權登錄。

第二種:如果網站 註冊功能的,也可以通過第三方授權登錄,授權成功之後,也可以直接把第三的用戶信息 註冊 保存到自己數據庫的 user 表裏面(但是密碼是後端自動生成的,用戶也不知道,只能用第三方授權登錄),這樣子的第三方的用戶和原生註冊的用戶信息都在同一張表了,這種情況得看自己項目的具體情況。筆者的博客網站暫時就採用了這種方式。

  • 2. 增加映射表

現實中很多網站都有多種賬戶登錄方式,比如可以用網站的註冊 id 登錄,還可以用手機號登錄,可以用 QQ 登錄等等。數據庫中都是有映射關係,QQ、手機號等都是映射在網站的註冊 id 上。保證不管用什麼方式登錄,只要去查映射關係,發現是映射在網站註冊的哪個 id 上,就讓哪個 id 登錄成功。

  • 3. 建立一個 oauth 表,一個 id 列,記錄對應的用戶註冊表的 id

建立一個 oauth 表,一個 id 列,記錄對應的用戶註冊表的 id,然後你有多少個第三方登陸功能,你就建立多少列,記錄第三方登陸接口返回的 openid;第三方登陸的時候,通過這個表的記錄的 openid 獲取 id 信息,如果存在通過 id 讀取註冊表然後用 session 記錄相關信息。不存在就轉向用戶登陸/註冊界面要用戶輸入本站註冊的賬戶進行 openid 綁定或者新註冊賬戶信息進行綁定。

具體代碼實踐請參考文章:

1. 第三方登錄用戶信息表設計

2. 淺談數據庫用戶表結構設計,第三方登錄

4. 最後

筆者的 github 博客地址:https://github.com/biaochenxuying/blog

如果您覺得這篇文章不錯或者對你有所幫助,請給個贊或者星唄,你的點贊就是我繼續創作的最大動力。

微信公衆號:BiaoChenXuYing
分享 前端、後端開發等相關的技術文章,熱點資源,隨想隨感,全棧程序員的成長之路。
關注公衆號並回復 福利 便免費送你視頻資源,絕對乾貨。
福利詳情請點擊: 免費資源獲取--Python、Java、Linux、Go、node、vue、react、javaScript

BiaoChenXuYing

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章