博客 / 詳情

返回

前後端分離項目OAuth第三方登錄怎麼做(以Github舉例)

OAuth是一種授權機制。OAuth過程中,系統會詢問數據所有者,是否同意授權第三方應用進入系統獲取這些數據,同意,則系統將產生一個短期的進入令牌(token),用來代替密碼,供第三方應用使用。

# OAuth流程(假設你的站點是A網站)
1. 用户事件觸發(一般點擊事件)跳轉,到 Github
2. Github 要求用户登錄,並詢問用户是同意 Github 下放授權碼給 A 網站
3. 用户同意,則 Github 重定向到 A 網站,同時攜帶一個授權碼(code,以url拼接形式下放)
4. A 網站使用授權碼(code)向 Github 請求令牌(token)
5. Github 返回令牌(token)
6. A 網站使用令牌,向 Github 請求用户數據
7. A 網站取得用户 Github 數據,前端展示登陸,後端存儲更新用户數據,並記錄登錄狀態

    本示例中,項目前後端分離開發,前端採用大家熟知的vue,後端採用koa2,OAuth邏輯和前後端框架其實無關,只會因為前後端分離在寫法和前後端不分離項目上略有不同。下面分別來説説進行OAuth開發前做的準備,以及在前後端項目中如何編寫代碼。

一、配置Github

鏈接:https://github.com/settings/a...

Application name:站點名稱,這裏假設是a

Homepage URL: 主站鏈接,本地測試,這裏是localhost:8001

Authorization callback URL:Github重定向鏈接,本地測試,這裏是localhost:8001/login

確認後,github將生成OAuth所需要的 clientID 和  clientSecret

二、前端編寫

// 將登錄方法封裝為插件 oauth.js
const getQuery = require('./getQuery') // 個人編寫用來獲取url query的方法

const oauthGithub = {
  getCode() {
    const authorize_uri = 'https://github.com/login/oauth/authorize';

    const client_id = 'b82f7274e2e996a2cecc';
    const redirect_uri = 'http://localhost:8001/login';

    location.href = `${authorize_uri}?client_id=${client_id}&redirect_uri=${redirect_uri}`;
  },
  async getUser(_this) {
    const code = getQuery('code');
    if (code) {
      try {
        const res = await _this.$axios.post(`/api/oauth/github?code=${code}`);
        if (res.data.errorno) {
          _this.$root.$emit('toast', { variant: 'danger', text: '登錄失敗' }); // 自定義toast
        } else {
          _this.$store.commit('setUser', res.data.data); // 存儲用户數據
          _this.$root.$emit('toast', { variant: 'success', text: '登錄成功' });
        }
      } catch(err) {
          _this.$root.$emit('toast', { variant: 'danger', text: '登錄失敗' });
      }
    }
  }
}

module.exports = {
  oauthGithub
}
// 在頁面或組件內調用插件方法
<template>
  <b-card>
    <b-row>
      <b-col md="4" class="border-right">
        <h5 class="pb-2 text-center">第三方登錄</h5>
        <div class="flex justify-center">
          <div class="pointer hover" @click="getCode()">
            <b-icon icon="github" font-scale="3"></b-icon>
            <p class="m-0 p-0">Github</p>
          </div>
        </div>
      </b-col>
    </b-row>
  </b-card>
</template>

<script>
  import { oauthGithub } from '~/plugins/oauth'

  export default {
    name: 'login',
    data() {
      return {}
    },
    mounted() {
      oauthGithub.getUser(this); // Github下放code重定向後調用
    },
    methods: {
      getCode() {
        oauthGithub.getCode(); // 點擊事件觸發,獲取code
      }
    }
  }
</script>

二、後端編寫

// 將OAuth單獨抽離為一個route:oauth.js
const router = require('koa-router')()
const axios = require('axios')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const {
  handleUser
} = require('../controller/oauth')

router.prefix('/api/oauth')

router.post('/github', async function (ctx, next) {
  const clientID = 'b82f7274e2e996a2cecc'
  const clientSecret = 'f09aa2=dfhf4ba57b6777307b6200d6d2a'

  const code = ctx.query.code
  const tokenRes = await axios({
    method: 'post',
    url: `https://github.com/login/oauth/access_token?client_id=${clientID}&client_secret=${clientSecret}&code=${code}`,
    headers: { accept: 'application/json' }
  })
  const accessToken = tokenRes.data.access_token
  if (accessToken) {
    const resData = await axios({
      method: 'get',
      url: `https://api.github.com/user`,
      headers: { accept: 'application/json', Authorization: `token ${accessToken}` }
    });
    const uname = resData.data.login // 用户github賬號名
    const reqBody = {
      nickname: resData.data.name, // 用户github暱稱
      avatar: resData.data.avatar_url, // 用户github頭像
      self_introduction: resData.data.bio, // 用户github自我介紹
      site: 'github', // 標識用户來自github
      site_id: resData.data.id, // 用户github id
      create_at: Date.now(), // 用户首次 OAuth 時間
      update_at: Date.now() // 用户最近一次 OAuth 時間
    }
    const userData = await handleUser(reqBody, uname) // controller方法,用户首次登錄則存儲用户,二次及以後登錄更新數據
    if (userData.id) {
      ctx.session.uid = userData.id //redis存儲登錄狀態
      ctx.body = new SuccessModel(userData)
    } else {
      ctx.body = new ErrorModel('登錄失敗')
    }
  }
})

module.exports = router

三、操作演示

  1. A 網站跳轉到 Github

  1. 用户確認後,GIthub 重定向到 A 網站,並攜帶 code

  1. 前端拿到URL query 部分的 code,向 Github 請求數據(token及用户數據獲取都在都在後端完成)
// 即route: oauth.js 部分邏輯
// 1. 拿到前端傳過來的code,向github請求token
// 2. 拿到token,向github請求用户數據
// 3. 拿到用户數據,將數據返回給前端
  1. 前端拿到用户數據,進行展示

原文鏈接:https://www.helloque.site/article/22

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.