Databasus 自托管实例的云端存储 OAuth 方案:经代理域名打通 Google Drive 授权
数据库灾备【免费下载链接】databasusPostgreSQL backup tool with Point-In-Time-Recovery and restore verification项目地址https://gitcode.com/gh_mirrors/po/databasus点击查看免费下载自托管Self-hosted的 Databasus 常常部署在http://localhost:4005这类 HTTP 甚至无静态 IP 的环境上而 Google Drive 等云存储的 OAuth 授权要求回调地址是可信的 HTTPS 域名——本文以 docs/how-extrnal-oauth-works.md 为核心讲清楚 Databasus 如何通过官方域名databasus.com作为代理把 OAuth 授权码“引回”自托管实例并沿着前端授权发起、回调换 Token、后端存储与自动续期的完整源码链路给出可验证的实现细节与关键参数。读完本文你将掌握OAuth 授权码模式在“无 HTTPS 回调域名”场景下的代理解决方案、Databasus 前端state参数中携带存储 DTO 的完整设计、以及后端 Google Drive 存储的 Token 校验、加密落库与 401 自动刷新机制。一、问题背景自托管实例为什么做不了标准 OAuth云存储普遍依赖 OAuth 完成用户授权Google Drive、Dropbox、OneDrive 等。标准 OAuth 授权码流程要求用户在浏览器中访问授权服务器如accounts.google.com授权成功后浏览器被重定向回应用方预先注册在 OAuth 客户端里的回调 URL应用再从该回调中拿到code。这里有两个硬性约束大多数 OAuth 服务要求回调地址使用 HTTPS 域名回调 URL 必须与在 OAuth 提供商如 Google Cloud Console注册的 URI 一致。而自托管的 Databasus 部署形态非常多样本地开发时是http://localhost:4005家庭网络里可能是动态 IP甚至根本没有公网域名。直接把http://localhost:4005/...注册进 Google OAuth 客户端既不被允许也无法被公网的 Google 服务器访问到。Databasus 的解法见 docs/how-extrnal-oauth-works.md是使用官方的主域名databasus.com作为永久注册的授权回调 URL。Google 把授权码重定向到这个 HTTPS 域名后该代理站点再把请求转发回用户的自托管实例从而让自托管前端拿到授权码完成后续换 Token 的操作。也就是说databasus.com充当了一个“域名桥”它对 Google 侧是稳定、可信的 HTTPS 回调地址对自托管实例侧它只是一次简单的重定向中转。二、整体授权时序原文档核心流程图docs/how-extrnal-oauth-works.md 给出的完整请求时序以 Google Drive 为例如下时序可以拆成四段自托管实例向 Google 发起授权请求并把自己需要的上下文存储配置 DTO编码进 OAuth 的state参数Google 授权成功后重定向到databasus.com/oauth携带code和原样带回的state代理站点把codestate一起重定向回自托管实例的回调路由/storages/google-oauth自托管前端用code直接调用 Google 的 Token 端点换回 access/refresh token随后把 Token 存入存储配置供后续备份文件交换使用。下面沿源码验证这条链路的每一步。三、授权发起前端构造 Auth URL 并打包 Storage DTO用户在 Databasus 前端添加 Google Drive 存储时由 EditGoogleDriveStorageComponent.tsx 负责发起授权。用户先填入自己 Google Cloud 客户端的clientId和clientSecret点击“Authorize”按钮后组件构造如下授权 URL源码 EditGoogleDriveStorageComponent.tsxhttps://accounts.google.com/o/oauth2/v2/auth ?client_id${clientId} redirect_uri${redirectUri} response_typecode scopehttps://www.googleapis.com/auth/drive.file access_typeoffline promptconsent state${encodeURIComponent(JSON.stringify(oauthDto))}其中各参数的作用与源码取值如下参数取值作用client_id用户填写的 Google OAuth 客户端 ID标识 OAuth 客户端redirect_uri${window.location.origin}/storages/google-oauth自托管实例自身的回调路由经代理转发后最终回到这里response_typecode授权码模式而非隐式返回 Tokenscopehttps://www.googleapis.com/auth/drive.file仅授予“应用创建的 Drive 文件”范围最小权限access_typeoffline关键让 Google 在换 Token 时返回 refresh tokenpromptconsent强制每次都弹出授权确认页stateJSON.stringify(oauthDto)序列化整个存储 DTO授权往返后原样带回state中携带的 DTO 由 StorageOauthDto.ts 定义export interface StorageOauthDto { storage: Storage; authCode: string; }这是本方案中相当巧妙的一处设计OAuth 的state参数本用于防 CSRF 和跨请求传状态Databasus 直接利用它在“浏览器被重定向”这一无状态过程中把即将创建的存储对象含 workspaceId、clientId、clientSecret 等完整地送回回调页回调页无需查询数据库就能知道这次授权属于哪个存储配置。四、回调换码/storages/google-oauth路由前端路由注册见 App.tsx/storages/google-oauth对应OauthStorageComponent。这正是时序图中“代理把 DTO auth code 重定向回自托管实例”的落点。OauthStorageComponent.tsx 的处理逻辑源码先从URLSearchParams中取出 Google 回传的code和state对state做decodeURIComponentJSON.parse还原出StorageOauthDto再把code赋给oauthDto.authCode校验oauthDto.storage.type StorageType.GOOGLE_DRIVE且存在googleDriveStorage配置否则提示unsupportedType调用exchangeGoogleOauthCode完成换码。值得注意的是从源码结构看当前实现只处理 Google Drive 一种存储类型其他类型走alert(t(app.oauthStorage.unsupportedType))分支这与原文档提到 “Google Drive, Dropbox, OneDrive” 的通用性描述相符方向但仓库内实际落地的是 Google Drive 链路。换 Token 的 HTTP 请求源码是标准的授权码换 Tokenconst redirectUri ${window.location.origin}/storages/google-oauth; const response await fetch(https://oauth2.googleapis.com/token, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded }, body: new URLSearchParams({ code: authCode, client_id: clientId, client_secret: clientSecret, redirect_uri: redirectUri, grant_type: authorization_code, }), });成功后把完整 Token 响应含access_token、refresh_token、expires_in序列化后写入oauthDto.storage.googleDriveStorage.tokenJson再弹出存储编辑表单EditStorageComponent让用户保存这条 Google Drive 存储——此时客户端凭据与 Token 一起落库对应时序图最后一步 “Store Google Drive config for files exchange”。失败路径也有明确处理Google 返回的错误细节只打到控制台英文技术性文案不直接展示给用户界面提示exchangeFailed3 秒后跳回首页。五、后端落库GoogleDriveStorage 模型与 Token 校验前端提交后存储配置在后端对应 google_drive/model.go 中的GoogleDriveStorage结构表为google_drive_storagestype GoogleDriveStorage struct { StorageID uuid.UUID json:storageId gorm:primaryKey;type:uuid;column:storage_id ClientID string json:clientId gorm:not null;type:text;column:client_id ClientSecret string json:clientSecret gorm:not null;type:text;column:client_secret TokenJSON string json:tokenJson gorm:not null;type:text;column:token_json }保存前有两道关键约束ValidateClientID、ClientSecret、TokenJSON三者缺一不可TokenJSON必须包含 refresh token代码会把TokenJSON反序列化为oauth2.Token并检查token.RefreshToken为空则报token JSON must contain a refresh token for automatic token refresh。这一点与前端发起授权时access_typeofflinepromptconsent的参数选择是严格配套的——只有离线访问模式才会返回 refresh token而后端后续的自动续期完全依赖它。此外ClientSecret与TokenJSON属于敏感字段保存前会经EncryptSensitiveData使用字段级加密器加密加密后带enc:前缀源码读取给前端时HideSensitiveData也会将其置空避免明文回显。六、Token 的生命周期自动刷新与文件交换授权完成后真正的价值在于后续的备份上传/下载。google_drive/model.go 中几个与 OAuth 直接相关的机制值得展开1. 401 自动刷新重试withRetryOnAuth每次 Drive 操作上传、下载、删除、连接测试都包裹在 withRetryOnAuth 中首次执行若命中认证错误isAuthError 识别 401 / Invalid Credentials / authError 等则调用 refreshToken 用存储的 refresh token 换取新 access token通过把Expiry强制设为过去时间来保证触发刷新刷新成功后重试原操作一次若刷新失败且属于invalid_grant/ refresh token 失效则返回明确提示要求用户重新授权并更新 Token 配置。2. 最小权限 scope 贯穿始终后端构造oauth2.Config时使用的 scope 与前端完全一致https://www.googleapis.com/auth/drive.file源码。这意味着 Databasus 只能读写自己创建的文件且所有备份都落在固定的databasus_backups文件夹内ensureBackupsFolderExists 会自动创建该文件夹。3. 上传/下载的工程细节上传采用 Google Drive 的 resumable upload分块大小gdChunkSize 16MB需为 256KB 的倍数源码并套了一层backpressureReader做背压控制下载经ResumingReader支持断点续传Range: bytesN-请求头 Content-Range校验downloadFileFromOffsetHTTP 客户端设置了连接/响应/空闲等超时30s/30s/90s源码。4. 连接测试即写读验证google_drive/model.go 的TestConnection会在databasus_backups文件夹内写入一个test-connection-uuid测试文件、读回比对内容、最后删除——它同时验证了 Token 有效性与文件夹权限可作为用户保存存储配置后的可用性与否的判据。七、与用户登录 OAuth 的区分仓库里存在第二条 OAuth 链路容易混淆这里明确区分OAuthCallbackPage.tsx 处理的是用户登录/注册的第三方登录state github或google时调用userApi.handleGitHubOAuth/userApi.handleGoogleOAuth把 code 交给后端完成用户态处理。它与本文讨论的存储授权/storages/google-oauth回调页在前端本地换 Token是两条独立的路由与流程前者建立用户会话后者建立云存储凭据。八、方案要点回顾代理桥接databasus.com作为永久注册的 HTTPS 回调地址把 Google 的重定向转发回任意自托管实例解决了 “localhost/无静态 IP 无法直接做 OAuth 回调” 的根本矛盾docs/how-extrnal-oauth-works.mdstate 承载 DTO整个StorageOauthDto存储配置 空的 authCode 占位序列化进state跨重定向无状态传递回调页据此精确完成换码与落库StorageOauthDto.ts、EditGoogleDriveStorageComponent.tsxoffline consent drive.fileaccess_typeoffline保证拿到 refresh tokenpromptconsent保证用户明确授权drive.file限定为应用级文件的最小权限refresh token 是硬校验后端Validate强制要求TokenJSON含 refresh token配合withRetryOnAuth的 401 自动刷新使一次授权可以长期支撑备份任务google_drive/model.go、google_drive/model.go敏感凭据加密落库clientSecret与tokenJson经字段级加密后存储不回显给前端google_drive/model.go。适用前提与限制该流程依赖 Databasus 官方代理域名的可用性以及用户在 Google Cloud 中创建 OAuth 客户端并正确配置重定向 URI当前仓库源码中完整实现的云端存储 OAuth 链路为 Google Drive其他云存储类型在回调页会被判定为不支持类型。赞分享数据库灾备【免费下载链接】databasusPostgreSQL backup tool with Point-In-Time-Recovery and restore verification项目地址https://gitcode.com/gh_mirrors/po/databasus点击查看免费下载相关推荐3分钟下载电子课本手把手带你解析教材链接、拿下PDF教材的完整教程3分钟下载电子课本手把手带你解析教材链接、拿下PDF教材的完整教程 开学前一晚一位初中语文老师把国家中小学智慧教育平台上的三本教材预览页挨个打开想把每册前网页爬虫教育validation-gen验证生成器高级应用自定义资源字段验证规则的终极实现指南validation gen验证生成器高级应用自定义资源字段验证规则的终极实现指南 在Kubernetes生态系统中 validation gen验证生成器Laf 云存储网站托管实战指南从桶托管到自定义域名自动 HTTPSLaf 云存储网站托管实战指南从桶托管到自定义域名自动 HTTPS Laf 云存储的「网站托管」功能允许开发者把任意静态站点上传到存储桶即自动生成独立访问域后端Serverless前端云原生上一篇Apache Burr生命周期钩子自定义扩展与插件开发完全指南下一篇手把手教你零基础搞定内网穿透Lucky公网神器实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考