切換語言為:簡體
網站引入 github 登入示例

網站引入 github 登入示例

  • 爱糖宝
  • 2024-07-24
  • 2076
  • 0
  • 0

建立授權應用

在 github 建立 OAuth Apps

網站引入 github 登入示例

這裏的 Authorization callback URL 就是後邊介面的 redirect_uri 引數

授權後返回的地址建議使用 history, hash 路由重定向後會有問題比如 http://localhost:5173/?code=079f6270a764f73036ed#/login

獲取 client_id、client_secret

網站引入 github 登入示例

找到對應的應用點選進入詳情

網站引入 github 登入示例

點選 Generate a new client secret 建立 client_secret

將獲取到的 client_id、client_secret 儲存一下後邊要用

授權流程

整體流程大概如下圖, 前端呼叫一個介面、後端呼叫兩個

網站引入 github 登入示例

我們來看下 github 官網文件猛擊訪問

網站引入 github 登入示例

文件有點不太好看懂,讓我們來看下 web 頁面如何完成授權登入

  1. 使用 https://github.com/login/oauth/authorize 填入相關引數重定向到 github 授權頁面

  2. 授權成功會攜帶 code 引數重定向到上邊設定的 redirect_uri 地址

  3. 前端獲取到路由中 code 引數向後端發起登入請求

  4. 後端接收到登入請求取出 code 向 https://github.com/login/oauth/access_token 發起 post 請求獲取 access_token 資訊

  5. 使用獲取到的 access_token 發起 https://api.github.com/user 請求獲取使用者資訊

  6. 後端返回登入成功資訊

前端實現

<script setup>
import { Icon } from '@iconify/vue'
import { useRoute, useRouter } from 'vue-router'
import { ElLoading } from 'element-plus'
import { ref } from 'vue'
import { useUserStore } from '@/stores'
import { githubLogin } from '@/api/login'

const loadingDom = document.getElementById('login-container') as HTMLElement

const loadingInstance = ref()
function setLoginLoading(message: string) {
  loadingInstance.value = ElLoading.service({
    target: loadingDom,
    text: message,
    background: 'rgba(0, 0, 0, 0.5)',
  })
}

const { VITE_APP_GITHUB_CLIENT_ID } = import.meta.env
function login() {
  /**
   * 介面引數檢視
   * https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
   */

  const client_id = VITE_APP_GITHUB_CLIENT_ID
  const redirect_uri = `${window.location.origin}/login`
  const path = `https://github.com/login/oauth/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}`
  window.open(path, '_self')

  setLoginLoading('正在獲取 github 授權...')
}

const { setLoginResData } = useUserStore()

const router = useRouter()

const route = useRoute()

const code = route.query.code as string

function loginGithub() {
  setLoginLoading('正在登入...')

  githubLogin({ code })
    .then((res) => {
      setLoginResData(res.data)
      ElMessage.success('登入成功!')
    })
    .catch(() => {
      ElMessage.warning('登入失敗!')
      router.replace('/login')
    })
    .finally(() => {
      loadingInstance.value?.close()
    })
}

// code 存在執行 github 登入邏輯
if (code)
  loginGithub()
</script>

<template>
  <el-button type="primary" class="w-[340px]" color="black" @click="login">
    <Icon icon="uil:github" class="cursor-pointer mr-4" width="24px" height="42px" />

    使用 GitHub 登入
  </el-button>
</template>

<style scoped>
</style>

client_id、redirect_uri 引數上邊有說明

網站引入 github 登入示例

授權頁面長這樣

網站引入 github 登入示例

拒絕授權會攜帶錯誤資訊重定向到上邊設定的 redirect_uri 頁面

網站引入 github 登入示例

確認授權會攜帶 code 引數重定向到上邊設定的 redirect_uri 頁面

https://kkdl.netlify.app/#/github?code=xxxxxxxx

授權範圍

scope 引數授權範圍不傳表示:授予對公共資訊的只讀訪問許可權(包括使用者個人資料資訊、儲存庫資訊和要點) 引數文件猛擊訪問

後端實現

後端使用 go、GoFrame 實現其他語言也幾乎一致 OAuth2.0 是一個通用協議

使用 OAuth2.0 可以簡化 github 介面的呼叫

安裝所需依賴

go get golang.org/x/oauth2
go get golang.org/x/oauth2/github

介面實現

package login

import (
  "context"
  "encoding/json"
  "github.com/gogf/gf/v2/errors/gerror"
  "github.com/gogf/gf/v2/frame/g"
  "golang.org/x/oauth2"
  "golang.org/x/oauth2/github"
  "net/http"
  "time"
)

// github 返回的使用者資訊
type GithubUserInfo struct {
  AvatarURL         string    `json:"avatar_url"`
  Bio               string    `json:"bio"`
  Company           string    `json:"company"`
  CreatedAt         time.Time `json:"created_at"`
  Email             *string   `json:"email"`
  EventsURL         string    `json:"events_url"`
  Followers         int       `json:"followers"`
  FollowersURL      string    `json:"followers_url"`
  Following         int       `json:"following"`
  FollowingURL      string    `json:"following_url"`
  GistsURL          string    `json:"gists_url"`
  GravatarID        string    `json:"gravatar_id"`
  Hireable          *bool     `json:"hireable"`
  HTMLURL           string    `json:"html_url"`
  ID                int       `json:"id"`
  Location          *string   `json:"location"`
  Login             string    `json:"login"`
  Name              string    `json:"name"`
  NodeID            string    `json:"node_id"`
  OrganizationsURL  string    `json:"organizations_url"`
  PublicGists       int       `json:"public_gists"`
  PublicRepos       int       `json:"public_repos"`
  ReceivedEventsURL string    `json:"received_events_url"`
  ReposURL          string    `json:"repos_url"`
  SiteAdmin         bool      `json:"site_admin"`
  StarredURL        string    `json:"starred_url"`
  SubscriptionsURL  string    `json:"subscriptions_url"`
  TwitterUsername   *string   `json:"twitter_username"`
  Type              string    `json:"type"`
  UpdatedAt         time.Time `json:"updated_at"`
  URL               string    `json:"url"`
}

func getGithubUserInfo(ctx context.Context, code string) (*GithubUserInfo, error) {
  // 讀取配置
  cfg := g.Cfg()
  githubOAuthConfig := &oauth2.Config{
   ClientID:     cfg.MustGet(ctx, "githubConfig.client_id").String(),
   ClientSecret: cfg.MustGet(ctx, "githubConfig.client_secret").String(),
   RedirectURL:  cfg.MustGet(ctx, "githubConfig.redirect_uri").String(),
   Scopes:       []string{"user"},
   Endpoint:     github.Endpoint, // 認證的端點這裏是 github 
  }

  token, err := githubOAuthConfig.Exchange(ctx, code)
  if err != nil {
   return nil, gerror.New("獲取 github token失敗!")
  }

  client := githubOAuthConfig.Client(ctx, token)
  
  // 獲取 github 使用者資訊
  resp, err := client.Get("https://api.github.com/user")
  
  if err != nil {
   return nil, gerror.New("獲取 github 使用者資訊失敗!")
  }
  defer resp.Body.Close()

  if resp.StatusCode != http.StatusOK {
   return nil, gerror.New("無法獲取 github 使用者資訊!")
  }

  var userInfo GithubUserInfo
  if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
   g.Log().Error(ctx, "Failed to parse user info:", err)
   return nil, gerror.New("格式化 github 使用者資訊失敗!")
  }

  return &userInfo, nil
}

網站引入 github 登入示例

獲取到 github 使用者資訊後做相應的業務處理

比如判斷使用者是否已經建立

  1. 未建立則建立使用者然後將使用者資訊返回前端登入成功

  2. 已建立則將查詢到的使用者資訊返回前端登入成功

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.