OAuth2 授权码模式-第三方登录

前言

我们今天看到某网站可以“使用 GitHub(或微信、微博)账号登录”,无需在该网站重新注册一个账号。这就是第三方登录,而背后的原理是一个名为 OAuth 2.0 的开放授权协议。

今天,我们就以“使用 GitHub 登录”为例,深入剖析 OAuth 2.0 的工作流程,理解 statecodetoken 这些关键概念的作用,看看它是如何安全地帮助我们授权,同时又保护我们的密码不被泄露的。

一、什么是 OAuth 2.0?

OAuth 2.0 是一个关于授权(Authorization) 的开放标准。注意,它不是认证(Authentication),这两者有细微,但重要的区别:

  • 认证(Authentication):是“证明你是谁”的过程,比如输入密码验证你是账号的主人。(登录)
  • 授权(Authorization):是“授予你特定权限”的过程,比如允许“某音乐App”访问你的微信头像和昵称。(授权)

OAuth 2.0 通过引入一个授权层(Authorization Layer),将“客户端”(A 网站)和“资源所有者”(你)分开。你无需提供密码,而是通过一个访问令牌(Access Token) 来授权,这个令牌有明确的权限范围和有效期,大大提升了安全性。

在 OAuth 2.0 的流程中,主要涉及四个角色:

  1. 资源所有者 (Resource Owner):就是你,用户。
  2. 客户端 (Client):就是想要使用你数据的应用,比如那个想用 GitHub 登录的 A 网站。
  3. 授权服务器 (Authorization Server):GitHub 专门负责处理登录和授权同意环节的服务器。用户在这里认证,并批准客户端的授权请求。
  4. 资源服务器 (Resource Server):GitHub 存放用户资源的服务器(如用户信息、仓库信息等)。它接收 Access Token 并返回受保护的资源。

对于 GitHub 来说,授权服务器和资源服务器通常是同一家,但在协议上是分开的概念。

二、OAuth 2.0 授权码流程(Authorization Code Flow)详解

“使用 GitHub 登录”采用的是 OAuth 2.0 中最安全、最常用的 授权码模式(Authorization Code Grant)。这个模式的核心是先拿到一个一次性的授权码(Code),再用这个码去换访问令牌(Access Token),令牌才是访问资源的钥匙。

为了让整个过程更加清晰,我们结合下面的流程图来逐步解析:

oauth2授权码模式流程

下面是每个步骤的详细说明:

步骤 1:用户点击“使用 GitHub 登录”
你在 A 网站点击这个按钮,A 网站(客户端)的登录流程就开始了。

步骤 2:重定向到授权服务器
A 网站会将你的浏览器重定向到 GitHub 的授权服务器的特定地址(如 https://github.com/login/oauth/authorize),并附带一系列查询参数,这些参数至关重要:

  • client_id: 必需。这是 A 网站在 GitHub 上注册 OAuth App 时获得的身份标识。GitHub 靠这个 ID 知道是哪个应用在请求授权。
  • redirect_uri: 可选但强烈建议。这是授权成功后,GitHub 应该将用户送回哪个地址(A 网站的回调地址)。必须在 GitHub OAuth App 设置中预先配置好,否则会被拒绝,这是一个重要的安全措施。
  • scope: 可选。定义了你希望授予的权限范围。例如 user:email 表示只获取读用户邮箱的权限,read:user 是获取读用户信息的权限。多个 scope 用空格分隔。
  • state: 强烈推荐。一个随机生成的、不可猜测的字符串。它是防止CSRF(跨站请求伪造)攻击的核心。A 网站生成这个字符串并暂时存起来(比如存在 Session 里),然后发送给 GitHub。当 GitHub 跳转回来时,会原封不动地带回这个 state。A 网站需要验证回来的 state 和之前发送的是否一致,以此证明这个回调请求确实来源于刚才的授权流程,而不是恶意攻击者伪造的。

一个完整的请求 URL 可能长这样:
https://github.com/login/oauth/authorize?client_id=abc123&redirect_uri=https://a-site.com/callback&scope=user:email&state=xyzABC123randomString

步骤 3:用户认证与授权
此时,你离开了 A 网站,来到了完全由 GitHub 控制的页面。如果你没有登录 GitHub,会先要求你输入 GitHub 的账号密码(注意:密码是输入给 GitHub 的,A 网站完全看不到!)。登录成功后,GitHub 会向你展示一个授权请求页面,列出 A 网站请求的权限(由 scope 定义),并询问你是否批准。

步骤 4:颁发授权码(Code)
如果你点击了“Authorize”(批准),GitHub 授权服务器就会将你的浏览器重定向回步骤 2 中指定的 redirect_uri,并在 URL 后附加两个重要参数:

  • code: 授权码。这是一个一次性的、有效期很短(通常几分钟) 的代码。它本身不代表任何权限,只是一个用来交换 Token 的凭证。
  • state: GitHub 会将之前 A 网站发来的 state 原样返回。

此时,浏览器的地址栏可能是:https://a-site.com/callback?code=xyz789&state=xyzABC123randomString

步骤 5:用授权码换访问令牌(Access Token)
这是最关键的一步,并且是在 A 网站的后端服务器与 GitHub 授权服务器的后端之间安全地进行的,用户的浏览器不参与。 A 网站的后端接收到回调请求后,会做以下几件事:

  1. 验证 State:比对收到的 state 参数和之前在 Session 中存储的值是否一致,以防止 CSRF 攻击。一致后才继续后续流程。
  2. 发送请求换取 Token:A 网站的后端向 GitHub 的令牌端点(如 https://github.com/login/oauth/access_token)发起一个 POST 请求(而非跳转),并携带以下信息:
    • client_id: A 网站的 ID。
    • client_secret: 这是 A 网站的密码,在注册 OAuth App 时获得。这个参数必须保密,绝不能在前端代码中泄露! 这也是为什么换取 Token 必须在后端进行。
    • code: 刚刚从回调 URL 中拿到的一次性授权码。
    • redirect_uri: 必须与请求 code 时使用的 redirect_uri 完全一致。

GitHub 授权服务器收到请求后,会验证 client_id, client_secret, coderedirect_uri 是否全部有效且匹配。如果一切正常,它会返回一个 JSON 响应,其中最重要的就是:

  • access_token: 访问令牌。这就是我们最终想要的“万能钥匙”。A 网站的后端现在可以用这个令牌去访问 GitHub 资源服务器上你授权范围内的资源了。
  • (可选)refresh_token: 刷新令牌。用于在 access_token 过期后,获取新的 access_token,而无需用户再次点击授权。

步骤 6:使用访问令牌获取资源
现在,A 网站的后端就可以拿着这把“钥匙”(Access Token)去向 GitHub 的资源服务器(API)请求你的信息了。例如,它想获取你的公开信息,可以发起一个 API 请求:
GET https://api.github.com/user
并在请求头中加入:Authorization: token gho_your_access_token_here

GitHub 资源服务器会验证 Token 的有效性和权限范围,如果通过,就会返回你的用户信息(ID、登录名、头像等)。

步骤 7:完成登录
A 网站的后端收到你的 GitHub 用户信息后,会用它来识别你的身份。

  • 如果这是你第一次通过 GitHub 登录,A 网站通常会使用你的 GitHub ID 和用户名等信息,为你自动创建一个本地账号并直接登录。
  • 如果你之前已经关联过,则直接登录你已有的账号。
    最后,A 网站会返回一个它自己网站的登录会话(Session 或它自己的 JWT Token)给你的浏览器,从而完成整个“第三方登录”流程。

三、除了授权码模式,还有别的模式吗?

有的,授权码模式(Authorization Code Grant)是 OAuth 2.0 核心规范(RFC 6749)中定义的四种标准授权模式之一。

你可以把 OAuth 2.0 框架想象成一个“工具箱”,里面提供了不同的“工具”(即授权模式)来应对不同的场景,而授权码模式是最初设计中最核心、最安全的模式。

授权模式 (Grant Type) 适用场景 典型例子
授权码模式 (Authorization Code) 最常用、最安全的模式,适用于有后端服务器的 Web 应用。令牌不会暴露给浏览器或用户。 任何网站上的“用XX登录”功能。
隐式模式 (Implicit) (已过时) 旧式用于纯前端/单页面应用(SPA)的模式,令牌直接返回给浏览器。因安全隐患已被 PKCE 扩展取代 旧版 JS 应用。
密码模式 (Resource Owner Password Credentials) 用户直接将用户名和密码交给客户端应用。非常不推荐,仅用于受信任的高权限应用(如官方客户端)。 自家公司出的移动App。
客户端凭证模式 (Client Credentials) 用于机器对机器的认证,客户端代表自己而不是用户去获取资源。 一个后台服务调用另一个API获取数据,与具体用户无关。

四、配置参数 OAUTH_GENERIC 有哪些呢?

OAUTH_GENERIC 这个名称通常出现在一些支持通用 OAuth 2.0 提供商的软件或平台中(例如 Portainer, Home Assistant, Grafana, Jenkins 等)。因为世界上有成千上万个服务实现了 OAuth 2.0,这些软件不可能为每一个都做预配置。

因此,它们提供了一个“通用”或“自定义”的配置选项,让你可以手动填入任何兼容 OAuth 2.0 的服务的参数。

以下是 OAUTH_GENERIC 或类似自定义配置中常见的核心参数:

参数名称 说明 举例/如何获取
Provider Name 给你的这个OAuth配置起个名字,用于标识。 My Company OAuth, Internal GitLab
Client ID (client_id) 必需。在授权服务器注册应用时获得的公开标识。 a1b2c3d4e5f6g7h8i9j0
Client Secret (client_secret) 必需。在授权服务器注册应用时获得的密钥,必须保密 a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
Authorization URL 必需。授权端点的URL,用户在这里登录和授权。 https://sso.mycompany.com/oauth/authorize
Token URL 必需。令牌端点的URL,应用后端用code来这里换token。 https://sso.mycompany.com/oauth/token
User Information URL (通常必需)。获取用户身份信息的API端点。这是 OpenID Connect 的概念,但通用OAuth也支持。 https://sso.mycompany.com/oauth/userinfo
Scope (scope) 可选。定义请求的权限范围。对于登录,通常需要能识别用户的scope,如 openid profile email

五、授权码模式的精妙之处总结

  1. 密码永不泄露:用户始终只在 GitHub 的页面上输入密码,A 网站无法接触到。
  2. 权限最小化:A 网站只能获取你明确授权(scope)的信息,而不能为所欲为。
  3. 核心安全措施
    • state 参数:防御 CSRF 攻击,是客户端必须实现的安全检查。
    • 授权码(Code)交换令牌(Token):关键的令牌(Token)是通过后端安全通道交换的,不会通过浏览器地址栏暴露。即使有人拦截到了 code,因为没有 client_secret,他也无法换到 access_token
    • client_secret 的保密:这是客户端身份的关键证明,必须保存在服务器端,绝不能写在移动应用或前端 JavaScript 代码中。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 8月20日,湖南银行在中国货币网发布2025半年报,披露上半年部分经营业绩。 报告显示,湖南银行今年上半年实现营业...
    财经野武士阅读 12评论 0 0
  • 柔雾豆沙:彩妆配方师的职场坚守与初心绽放 一、试色盘前的“新人下马威” 夏晚抱着装满自制唇釉小样的帆布包,走进“星...
    繁华三千不过云烟阅读 21评论 0 1
  • 我的“暑假作业”:读文学名著 去年什么时候,写过一篇《“军大衣”和“小棉袄”》的文章,表达我们对于有了“小棉袄”之...
    吟啸斋主阅读 29评论 0 0
  • 家裏賬單 最近收入將近五千元錢,賣六七袋複合肥需要一千元錢,七八口人交新農和需要三千多遠,半...
    野鸡和猫和老鷹阅读 17评论 0 0
  • 家裏賬單 最近收入將近五千元錢,賣六七袋複合肥需要一千元錢,七八口人交新農和需要三千多遠,半...
    野鸡和猫和老鷹阅读 19评论 0 0