返回文章列表

文章

使用Github第三方登录完整流程

总体流程如下: 用户点击登录 → 跳转 GitHub → 授权 → 回调 code → 后端换 token → 拉取用户信息 → 生成 JWT → 重定向前端 → 前端保存 JWT → 后续 API 调用

目录
  1. 使用 GitHub 进行第三方登录(OAuth2)的完整流程整理如下,以 Actix + Rust + JWT 的实现为示例。
  2. 1️⃣ 准备工作
  3. 2️⃣ 登录入口
  4. 3️⃣ 用户授权
  5. GET /auth/github/callback?code=xxxx&state=xxxx ```
  6. 4️⃣ 后端换取 Github Access Token
  7. 5️⃣ 使用Github Access Token获取Github的用户信息
  8. 6️⃣ 生成自己应用系统的JWT(无需 session)
  9. 7️⃣ 重定向到前端
  10. 8️⃣ 前端获取并使用JWT 调用 API

使用 GitHub 进行第三方登录(OAuth2)的完整流程整理如下,以 Actix + Rust + JWT 的实现为示例。#

1️⃣ 准备工作#

  1. 注册 GitHub OAuth App
    • 地址:https://github.com/settings/developers
    • 填写:
      • Application name: 项目名称
      • Homepage URL: 前端主页,如 http://localhost:3000
      • Authorization callback URL: GitHub 登录后重定向的后端 URL,如 http://localhost:8000/auth/github/callback
    • 获取:
      • Client ID
      • Client Secret
  2. 后端环境变量
    • GITHUB_CLIENT_ID
    • GITHUB_CLIENT_SECRET
    • FRONTEND_URL (前端页面 URL,用于登录成功后跳转)
    • DATABASE_URL (可选,如果存用户信息)

2️⃣ 登录入口#

用户点击 “使用 GitHub 登录” 按钮:

  1. 后端生成 OAuth2 授权 URL
    • 包含:
      • client_id
      • redirect_uri(回调地址)
      • scope(请求权限,如 user:email
      • state(防 CSRF,可随机字符串)
      • code_challenge(如果使用 PKCE)
  2. 返回这个 URL 给前端,前端跳转到 GitHub 授权页面。
// Actix 例子
let (auth_url, csrf_state, pkce_code_challenge) = client
    .authorize_url(CsrfToken::new_random)
    .add_scope(Scope::new("user:email".to_string()))
    .set_pkce_challenge(PkceCodeChallenge::from_code_verifier(&pkce_verifier))
    .url();

3️⃣ 用户授权#

  • 用户在 GitHub 页面点击 Authorize
  • GitHub 验证通过后,重定向到你的 callback URL

GET /auth/github/callback?code=xxxx&state=xxxx ```#

4️⃣ 后端换取 Github Access Token#

  1. 后端收到 codestate
  2. 验证 state 是否和之前生成的一致(防 CSRF)。
  3. 如果使用 PKCE,需要使用 code_verifier
  4. 请求 GitHub Token 接口:
let token_res = client
    .exchange_code(AuthorizationCode::new(code.to_string()))
    .set_pkce_verifier(pkce_verifier)
    .request_async(oauth2::reqwest::async_http_client)
    .await;
  • 返回:
    • access_token
    • scope
    • token_type

如果失败,就返回错误给前端(JSON + UTF-8),不要直接显示中文文本,避免乱码。


5️⃣ 使用Github Access Token获取Github的用户信息#

let user_res = HTTP
    .get("https://api.github.com/user")
    .bearer_auth(token.access_token().secret())
    .header("User-Agent", "your-app-name")
    .send()
    .await?;
  • 获取:
    • login / id / name / avatar_url / email
  • 如果邮箱为空,还可以调用:
GET https://api.github.com/user/emails

6️⃣ 生成自己应用系统的JWT(无需 session)#

  • 使用用户信息生成 JWT:
let jwt = generate_jwt(&user);
  • JWT 可以包含:
    • 用户 ID
    • 登录来源(GitHub)
    • 过期时间
  • 后端不再维护 session,前端保存 JWT(localStorage / cookie)。

7️⃣ 重定向到前端#

  • 登录成功后,重定向到前端页面,并带上 JWT:
let frontend_url = env::var("FRONTEND_URL").unwrap_or_else(|_| "http://localhost:3000".to_string());
let url = format!("{frontend_url}?token={jwt}");

HttpResponse::Found()
    .append_header(("Location", url))
    .finish();

最后浏览器就会跳转,如下链接去访问前端资源,并在地址栏传递参数

http://localhost:3000?token=eyJhbGciOi...

⚠️ 注意: - 如果 JWT 里有中文信息,要保证 HTTP header Content-Type 或 JSON 返回中 charset=utf-8,否则浏览器显示乱码。 - 前端拿到 JWT 后解析,存入 localStorage 或 cookie 进行后续 API 调用认证。


8️⃣ 前端获取并使用JWT 调用 API#

  • 前端从 URL(也就是上一节说的后端跳转的FRONTEND_URL) 获取 JWT。
import { useEffect } from "react";
import { useLocation, useNavigate } from "react-router-dom";

export function OAuthCallbackHandler() {
  const location = useLocation();
  const navigate = useNavigate();

  useEffect(() => {
    const params = new URLSearchParams(location.search);
    const token = params.get("token");

    if (token) {
      // 存储到 localStorage 或 sessionStorage
      localStorage.setItem("access_token", token);

      // 移除 URL 上的敏感 token,防止被历史记录
      navigate("/", { replace: true });
    }
  }, [location, navigate]);

  return <div>登录中,请稍候...</div>;
}