Github开通第三方平台OAuth登录及Java对接步骤

调研起因:
准备搞AI Agent海外项目,有相当一部分用户群体是程序员,所以当然要接入Github这个全球最大的同性交友网站了,让用户使用Github账号一键完成注册或登录。

本教程基于Web H5界面进行对接,同时也提供了spring-boot版本的对接Demo在:
https://github.com/youbl/study/tree/master/study-codes/github-oauth-login-demo

废话不多说,直接开始步骤说明吧:
注意:要注册和对接,国内访问github不那么稳定,自己想办法保证稳定性吧。

1、Github OAuth功能申请

1.1、账号注册

首先肯定是要有一个有效的Github账号,去这里注册去吧:https://github.com/signup
这个过程比较简单,输入一个邮箱和密码,接收验证码邮件确认即可,过程中会进行机器人验证。
如果已经有账号了,可以忽略。

1.2、创建Github App

按Github要求,要在App内配置和使用OAuth身份验证能力,所以要先创建Github App。
注:Github支持2种App:
在这里插入图片描述
两种Apps的区别参考官方说明:https://docs.github.com/zh/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps
我自己实际配置对比了一下:

  • Github Apps:推荐,支持配置最多10个回调地址,支持细粒度的权限配置;
  • OAuth Apps:Github也不推荐,只支持配置1个回调地址(不方便测试),不支持权限配置。

Github Apps创建说明参考:https://docs.github.com/zh/apps/creating-github-apps/registering-a-github-app/registering-a-github-app

  • 先登录并进入Github首页:https://github.com/
    点击右上角的头像,再点弹出菜单下面的“Settings”:
    在这里插入图片描述
  • 再点左边菜单列表最下面的“Developer settings”:
    在这里插入图片描述
  • 点击右侧的“New GitHub App”
    在这里插入图片描述
  • 在新建窗口,输入必要信息,必填项:
    • GitHub App name:应用名,自己写,必须在GitHub唯一才行;
    • Homepage URL:你的网站地址,没有可以使用Github项目地址,不过Github没有校验是否存在;
    • Callback URL:Github登录成功的回调地址,最多可以配置10个,建议把开发、测试、预上、生产都配置进去;
    • Webhook:取消勾选Active,不接收Github的活动事件;
    • 最后点击最下方的“Create GitHub App”即可。
      在这里插入图片描述
  • 此时会进入创建成功的App页面,点击页面右边的“Generate a new client secret”, 生成一个OAuth使用的密钥,请务必把这个Client ID和Client secret复制并保存下来,后面程序要用:
    在这里插入图片描述
  • OK,到这里,GitHub App已经创建完成了,下面是代码对接过程。
    注:一开始还以为要配置权限,添加“Email addresses”的只读权限,后面发现并不需要。

2、代码对接

上面的步骤操作完成后,有了客户端ID和Secret,把它复制下来,开始用于我们的代码对接。

2.1、技术方案和时序图

考虑前端灵活性,以及code无法单独生成access_token,不存在安全问题,下面演示的方案使用前端接收GitHub回调,再通过后端生成access_token及获取用户信息,时序图如下:
在这里插入图片描述

2.2、前端页面对接代码生成

可以参考官方的对接文档说明:https://docs.github.com/zh/apps/creating-github-apps/writing-code-for-a-github-app/building-a-login-with-github-button-with-a-github-app#add-code-to-generate-a-user-access-token

2.2.1、登录页面的按钮逻辑

这一步就是生成一个按钮,点击跳转去GitHub登录授权页,参考代码:

<input type="button" onclick="doLogin" value="Github登录">
<script type="text/javascript">const githubUrl = 'https://github.com/login/oauth/authorize?scope=user:email&client_id=';const githubClientId = '上面App里的 Client ID';const redirectUri = 'http://127.0.0.1:8999/demo/githubCallback.html';function doLogin() {location.href = githubUrl + encodeURIComponent(githubClientId)+ '&redirect_uri=' + encodeURIComponent(redirectUri);}
</script>

注:上面代码里的回调地址 http://127.0.0.1:8999/demo/githubCallback.html 要记得在GitHub Apps那边的Callback URL里配置,否则点登录时会报错:
在这里插入图片描述

2.2.2、上面提到的回调地址逻辑

http://127.0.0.1:8999/demo/githubCallback.html 这个回调页面,要从url里提取出授权码code,并转发给服务端,
回调页面代码参考:

<script>start();function start() {let code = getFromSearch('code');if (!code) {return alert('未找到code授权码:' + url);}// 后端获取github用户信息getUserInfoByServer(code);}/*** 从url的参数里获取变量值* @param name 变量名* @returns {string} 变量值*/function getFromSearch(name) {if (!name || !location.search || location.search.length <= 1) {return ''; // 没有search}let array = location.search.substring(1).split('&'); // 去除第一个 ?for (let i = 0, j = array.length; i < j; i++) {let item = array[i];let idx = item.indexOf('=');if (idx <= 0) continue;if (name === item.substring(0, idx))return item.substring(idx + 1);}return '';}function getUserInfoByServer(code) {let userInfoUrl = '../callback?code=' + encodeURIComponent(code);var xhr = new XMLHttpRequest();xhr.open('GET', userInfoUrl);xhr.onreadystatechange = function (e) {if (xhr.response) {const data = JSON.parse(xhr.response);document.getElementById('txtUserInfoServer').value = '后端返回得到的github用户信息:\r\n' +JSON.stringify(data, null, 4);}};xhr.send(null);}
</script>

2.3、后端根据code获取用户信息的逻辑

2.3.1、根据code生成access_token

要使用code + Client ID + Client secret,才能生成access_token,
对应的API文档:https://docs.github.com/zh/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app#using-the-web-application-flow-to-generate-a-user-access-token

参考代码:

@FeignClient(name = "github-token", url = "https://github.com")
public interface GithubTokenFeign {/*** 根据登录成功回调的code,交换access_token* @param dto code信息* @return token信息*/@PostMapping(value = "login/oauth/access_token", headers = {"Accept=application/json", "Content-Type=application/json"})GithubTokenOutputDto getAccessToken(GithubTokenInputDto dto);/*
// 异常场景:
// 不加 Accept=application/json ,返回值如下:
// error=bad_verification_code&error_description=The+code+passed+is+incorrect+or+expired.&error_uri=https%3A%2F%2Fdocs.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-oauth-app-access-token-request-errors%2F%23bad-verification-code
// 加了返回值如下:
// {"error":"bad_verification_code","error_description":"The code passed is incorrect or expired.","error_uri":"https://docs.github.com/apps/managing-oauth-apps/troubleshooting-oauth-app-access-token-request-errors/#bad-verification-code"}
// 正常场景,返回的json如下:
// {access_token=aaa, expires_in=28800, refresh_token=bbb, refresh_token_expires_in=15811200, token_type=bearer, scope=}* */
}

2.3.2、根据access_token获取用户信息

对应的API文档:https://docs.github.com/zh/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user

参考代码:

@FeignClient(name = "github-api", url = "https://api.github.com")
public interface GithubApiFeign {/*** 获取用户信息* @param authorization access_token* @return 用户信息*/@GetMapping(value = "user", headers = {"Accept=application/json", "Content-Type=application/json"})GithubUserDto getUserInfo(@RequestHeader String authorization);/*成功响应参考:
cost time(ms): 916 status:200 from GET https://api.github.com/user
Headers:access-control-allow-origin: *access-control-expose-headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunsetcache-control: private, max-age=60, s-maxage=60content-length: 1250content-security-policy: default-src 'none'content-type: application/json; charset=utf-8date: Tue, 29 Apr 2025 10:00:16 GMTetag: "abc"github-authentication-token-expiration: 2025-04-29 18:00:09 UTClast-modified: Mon, 28 Apr 2025 09:48:55 GMTreferrer-policy: origin-when-cross-origin, strict-origin-when-cross-originserver: github.comstrict-transport-security: max-age=31536000; includeSubdomains; preloadvary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-Withx-accepted-github-permissions: allows_permissionless_access=truex-accepted-oauth-scopes:x-content-type-options: nosniffx-frame-options: denyx-github-api-version-selected: 2022-11-28x-github-media-type: github.v3x-github-request-id: abcx-oauth-client-id: abcx-oauth-scopes:x-ratelimit-limit: 5000x-ratelimit-remaining: 4999x-ratelimit-reset: 1745924416x-ratelimit-resource: corex-ratelimit-used: 1x-xss-protection: 0Body:
{"login":"youbl","id":1,"node_id":"abc","avatar_url":"https://avatars.githubusercontent.com/u/2508702?v=4","gravatar_id":"","url":"https://api.github.com/users/youbl","html_url":"https://github.com/youbl","followers_url":"https://api.github.com/users/youbl/followers","following_url":"https://api.github.com/users/youbl/following{/other_user}","gists_url":"https://api.github.com/users/youbl/gists{/gist_id}","starred_url":"https://api.github.com/users/youbl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/youbl/subscriptions","organizations_url":"https://api.github.com/users/youbl/orgs","repos_url":"https://api.github.com/users/youbl/repos","events_url":"https://api.github.com/users/youbl/events{/privacy}","received_events_url":"https://api.github.com/users/youbl/received_events","type":"User","user_view_type":"public","site_admin":false,"name":"水边","company":"@baidu","blog":"http://beinet.cn","location":"Fuzhou","email":"youbl@126.com","hireable":true,"bio":"https://youbl.blog.csdn.net/","twitter_username":null,"notification_email":"youbl@126.com","public_repos":17,"public_gists":0,"followers":12,"following":0,"created_at":"2012-10-08T03:37:21Z","updated_at":"2025-04-28T09:48:55Z"}*/
}

2.3.3、封装API给前端调用

参考代码:

@RestController
@RequiredArgsConstructor
public class GithubCallbackController {private final String githubClientId = "Iv23liwz6AxRv7VXcHvf";private final String githubClientSecret = "github app里的Client secret";private final GithubTokenFeign githubTokenFeign;private final GithubApiFeign githubApiFeign;// 会带code回调,如 http://localhost:8080/callback?code=abc@GetMapping("callback")public GithubUserDto callback(@RequestParam String code) {GithubTokenInputDto dto = new GithubTokenInputDto().setClient_id(githubClientId).setClient_secret(githubClientSecret).setCode(code);// 根据授权码,获取access_tokenGithubTokenOutputDto ret = githubTokenFeign.getAccessToken(dto);if (!ret.success()) {throw new RuntimeException("failed: " + ret.getError_description() + " " + ret.getError_uri());}String auth = ret.getToken_type() + " " + ret.getAccess_token();// 根据access_token, 获取用户信息return githubApiFeign.getUserInfo(auth);}
}

2.4、运行和测试

把项目跑起来,在浏览器里访问:http://127.0.0.1:8999/login.html

  • 打开的登录测试页如下:
    在这里插入图片描述

  • 点击上面的登录按钮,会进入GitHub的授权页面:
    注:每个用户只会显示一次,授权通过后,第二次再登录,就没有这个界面了,会直接进入回调页;
    除非该用户去GitHub的设置页面,删除授权,删除授权的地址:https://github.com/settings/apps/authorizations
    在这里插入图片描述

  • 点击上面授权页的“Authorize xxx”后,就会调用后端去获取GitHub的用户信息了,测试页面的结果如下:
    在这里插入图片描述

3、常见问题

3.1、不支持iframe嵌入

测试中发现,想在登录页面弹出一个浮层,使用iframe嵌入GitHub的授权页面是不支持的,必须是跳转过去完成授权,再通过Callback URL接收回调的方式。
因此,我项目里的其它登录,比如Google OAuth登录也要修改风格,从弹小窗变成跳转方式了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/79108.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/79108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

期刊、出版社、索引数据库

image 1、研究人员向期刊或者会议投稿&#xff0c;交注册费和相应的审稿费等相关费用[1]&#xff1b; 2、会议组织者和期刊联系出版社&#xff0c;交出版费用&#xff1b; 3、出版社将论文更新到自己的数据库中&#xff0c;然后将数据库卖给全世界各大高校或企业&#xff1b; 4…

Transformer 模型及深度学习技术应用

近年来&#xff0c;随着卷积神经网络&#xff08;CNN&#xff09;等深度学习技术的飞速发展&#xff0c;人工智能迎来了第三次发展浪潮&#xff0c;AI技术在各行各业中的应用日益广泛。 注意力机制&#xff1a;理解其在现代深度学习中的关键作用&#xff1b; Transformer模型…

zynq7035的arm一秒钟最多可以支持触发多少次中断

一、概述 1.关于zynq7035的ARM处理器一秒能够支持多少次中断触发&#xff0c;需要综合来考虑。需要确定ARM处理器的参数&#xff0c;目前zynq7000系列&#xff0c;使用的双核Cortex-A9处理器。其中主频大概在500MHZ~1GHZ左右&#xff0c;不同的用户配置的主频可能稍微有差别。 …

数据结构与算法:图论——最短路径

最短路径 先给出一些leetcode算法题&#xff0c;以后遇见了相关题目再往上增加 最短路径的4个常用算法是Floyd、Bellman-Ford、SPFA、Dijkstra。不同应用场景下&#xff0c;应有选择地使用它们&#xff1a; 图的规模小&#xff0c;用Floyd。若边的权值有负数&#xff0c;需要…

[android]MT6835 Android 关闭selinux方法

Selinux SELinux is an optional feature of the Linux kernel that provides support to enforce access control security policies to enforce MAC. It is based on the LSM framework. Working with SELinux on Android – LineageOS Android 关闭selinux MT6835 Android…

【Linux网络编程】http协议的状态码,常见请求方法以及cookie-session

本文专栏&#xff1a;Linux网络编程 目录 一&#xff0c;状态码 重定向状态码 1&#xff0c;永久重定向&#xff08;301 Moved Permanently&#xff09; 2&#xff0c;临时重定向&#xff08;302 Found&#xff09; 二&#xff0c;常见请求方法 1&#xff0c;HTTP常见Hea…

当神经网络突破摩尔定律:探索大模型时代的算力新纪元

当摩尔定律熄灭后&#xff1a;AI算力革命如何重塑技术文明的底层逻辑 一、摩尔定律的黄昏&#xff1a;物理极限与经济理性的双重困境 当英特尔在1965年提出摩尔定律时&#xff0c;没有人预料到这个每18-24个月将芯片晶体管数量翻倍的预言会成为现代计算文明的基石。半个世纪以…

位运算题目:寻找重复数

文章目录 题目标题和出处难度题目描述要求示例数据范围进阶 前言解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 解法三思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;寻找重复数 出处&#xff1a;287. 寻找重复数 难度 6 级 题目描述 要…

Elasticsearch:没有 “AG” 的 RAG?

作者&#xff1a;来自 Elastic Gustavo Llermaly 了解如何利用语义搜索和 ELSER 构建一个强大且视觉上吸引人的问答体验&#xff0c;而无需使用 LLMs。 想要获得 Elastic 认证&#xff1f;查看下一期 Elasticsearch Engineer 培训的时间&#xff01; Elasticsearch 拥有众多新…

linux下安装ollama网不好怎么办?

文章目录 前言kkgithub下载脚本,而不是直接运行修改脚本修改权限还是不行?前言 今天想在linux上面更新一下ollama,于是去到官网: https://ollama.com/download/linux linux下安装ollama还是挺简单的: curl -fsSL https://ollama.com/install.sh | sh我也是特别嗨皮地就…

相机-IMU联合标定:相机-IMU外参标定

文章目录 📚简介🚀标定工具kalibr🚀标定数据录制🚀相机-IMU外参标定📚简介 在 VINS(视觉惯性导航系统) 中,相机-IMU外参标定 是确保多传感器数据时空统一的核心环节,其作用可概括为以下关键点: 坐标系对齐(空间同步),外参误差会导致视觉特征点投影与IMU预积…

基于 Java 的实现前端组装查询语句,后端直接执行查询方案,涵盖前端和后端的设计思路

1. 前端设计 前端负责根据用户输入或交互条件,动态生成查询参数,并通过 HTTP 请求发送到后端。 前端逻辑: 提供用户界面(如表单、筛选器等),让用户选择查询条件。将用户选择的条件组装成 JSON 格式的查询参数。发送 HTTP 请求(如 POST 或 GET)到后端。示例: 假设用…

[STM32] 4-2 USART与串口通信(2)

文章目录 前言4-2 USART与串口通信(2)数据发送过程双缓冲与连续发送数据发送过程中的问题 数据接收过程TXE标志位&#xff08;发送数据寄存器空&#xff09;TC标志位&#xff08;发送完成标志位&#xff09;单个数据的发送数据的连续发送 接收过程中遇到的问题问题描述&#xf…

Qt多线程TCP服务器实现指南

在Qt中实现多线程TCP服务器可以通过为每个客户端连接分配独立的线程来处理&#xff0c;以提高并发性能。以下是一个分步实现的示例&#xff1a; 1. 自定义工作线程类&#xff08;处理客户端通信&#xff09; // workerthread.h #include <QObject> #include <QTcpSo…

详细介绍Python-pandas-DataFrame全部 *功能* 函数

Python-pandas-DataFrame全部 功能 函数 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是pandas的使用语法。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性。【帮帮志系列文章】&#xff1a;每个知识点…

香港科技大学广州|可持续能源与环境学域博士招生宣讲会—四川大学专场

香港科技大学广州&#xff5c;可持续能源与环境学域博士招生宣讲会—四川大学专场 时间&#xff1a;2025年5月8日&#xff08;星期四&#xff09;16:30开始 地点&#xff1a;四川大学基础教学楼A座504 宣讲嘉宾&#xff1a;肖殿勋 助理教授 一经录取&#xff0c;享全额奖学金…

装饰器设计模式(Decorator Pattern)详解

装饰器设计模式(Decorator Pattern)详解 装饰器模式是一种结构型设计模式,它允许动态地向对象添加额外行为,而无需修改其原始类。这种模式通过包装对象的方式提供灵活的扩展功能替代继承。 1. 核心概念 (1)模式定义 装饰器模式:动态地给一个对象添加一些额外的职责,就…

【SpringMVC】详解参数传递与实战指南

目录 1.前言 2.正文 2.1基础参数传递 2.1.1单参数 2.1.2多参数 2.2对象参数绑定 2.2.1自动封装对象 2.2.2参数别名处理 2.3集合类型处理 2.3.1数组接收 2.3.2List集合接收 2.4JSON参数处理 2.4.1介绍JSON 2.4.2传递JSON参数 2.5RESTful风格参数 2.6文件上传处理…

mysql-窗口函数一

目录 一、感受一下分组与窗口函数的区别 二、滑动窗口&#xff08;子窗口&#xff09;大小的确认 2.1 分组函数下order by使用 2.2 窗口子句 2.3 执行流程 三、函数使用 窗口函数需要mysql的版本大于等于8才行&#xff0c;可以先检查一下自己的mysql版本是多少 select ve…

解决在Mac上无法使用“ll”命令

在 macOS 上&#xff0c;ll 命令是一个常见的别名&#xff0c;它通常是指向 ls -l 的。但是&#xff0c;如果你看到 zsh: command not found: ll&#xff0c;这意味着你当前的 zsh 配置中没有设置 ll 作为别名。 解决方法&#xff1a; 1. 使用 ls -l 命令 如果只是想查看目录…