网站集成 github 登录太简单了!

创建授权应用

在 github 创建 OAuth Apps

这里的 Authorization callback URL 就是后边接口的 redirect_uri 参数

授权后返回的地址建议使用 history, hash 路由重定向后会有问题比如 http://localhost:5173/?code=079f6270a764f73036ed#/login

获取 client_id、client_secret

找到对应的应用点击进入详情

点击 Generate a new client secret 创建 client_secret

将获取到的 client_id、client_secret 保存一下后边要用

授权流程

整体流程大概如下图, 前端调用一个接口、后端调用两个

我们来看下 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. 后端返回登录成功信息

kkdl 体验地址猛击访问 因为后端服务部署在阿里云所以能不能访问 github 接口全凭天意!

前端实现

html 复制代码
<script lang="ts" 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 lang="scss" scoped>
</style>

client_id、redirect_uri 参数上边有说明

授权页面长这样

拒绝授权会携带错误信息重定向到上边设置的 redirect_uri 页面

确认授权会携带 code 参数重定向到上边设置的 redirect_uri 页面

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

授权范围

scope 参数授权范围不传表示:授予对公共信息的只读访问权限(包括用户个人资料信息、存储��信息和要点) 参数文档猛击访问

后端实现

后端使用 go、GoFrame 实现其他语言也几乎一致 OAuth2.0 是一个通用协议

使用 OAuth2.0 可以简化 github 接口的调用

安装所需依赖

bash 复制代码
go get golang.org/x/oauth2
go get golang.org/x/oauth2/github

接口实现

go 复制代码
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 用户信息后做相应的业务处理

比如判断用户是否已经创建

  1. 未创建则创建用户然后将用户信息返回前端登录成功
  2. 已创建则将查询到的用户信息返回前端登录成功

总结

本文实践了 web 网站如何集成 github 登录,前后端代码均已开源

前端代码仓库

后端代码仓库

相关推荐
paopaokaka_luck1 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
逐·風2 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫3 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
码农小旋风3 小时前
详解K8S--声明式API
后端
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml43 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~3 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616883 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
尚梦3 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子4 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js