OAuth 2.0 是一种让第三方网站在不用知道你密码的情况下,安全获取你数据的授权方法。它的核心思想就是用令牌(Token)代替密码。为了让你轻松理解,我们先认识 4 个“小角色”,再看最常用的授权码流程(Authorization Code Flow)。
核心概念
核心角色
- 用户(Resource Owner):比如你自己。
- 客户端(Client):比如你想登录的第三方游戏网站。
- 认证服务器(Authorization Server):比如微信或 Google 的登录系统,负责发令牌。
- 资源服务器(Resource Server):比如微信的服务器,里面有你的头像和好友列表。
授权码模式(Authorization Code Flow)
这是网上最常见、最安全的流程,比如你用微信登录某个新游戏。
1. 客户端发起请求
你想玩一个新游戏。你点击了游戏网站上的 “用微信登录” 按钮。
2. 跳转到认证页面
游戏网站把你送到了微信的登录页面。这时候,网址里会带着游戏网站的身份证(Client ID)和跳回来的地址(Redirect URI)。
3. 用户同意授权
你在微信页面上输入账号密码登录,微信会问你:“你同意该游戏获取你的头像和名字吗?” 你点击“同意”。
4. 服务器发放授权码
微信确认你同意后,把你的浏览器送回游戏网站。同时,微信在网址后面塞了一个临时的授权码(Code)。
5. 客户端用授权码换令牌
游戏网站悄悄拿着这个授权码,在后台去找微信的认证服务器。微信核对无误后,把真正的访问令牌(Access Token)发给游戏网站。
6. 获取受保护的资源
游戏网站拿着访问令牌,去找微信的资源服务器。微信服务器检查令牌是真的,就把你的头像和名字给游戏网站,登录成功!

为什么不直接发令牌,非要先发“授权码”?
因为步骤 4 的网址跳转是在浏览器上发生的,容易被坏人偷看。
- 授权码是一张临时的“单据”,别人偷走了也没有用。
- 只有游戏网站在后台用自己的秘密钥匙(Secret),才能把授权码换成真正的访问令牌。这样非常安全!
游戏网站必须自己在后端实现“用 Code 换 Token”
微信、Google 等平台只负责提供接口,游戏网站需要自己编写代码去调用这个接口。为了安全,这一步流程和核心代码通常是这样实现的。
游戏网站需要做的 3 件事
1. 准备好“接头暗号”
游戏网站在后台代码里,必须存好两个关键信息(这是事先在微信/Google 开放平台注册得到的):
- Client ID(应用标识):相当于公开的用户名。
- Client Secret(应用密钥):相当于密码,绝对不能泄露给前端浏览器。
2. 后端提供一个“接收 Code”的接口
当用户在微信点击同意后,微信会把 Code 发送到游戏网站指定的网址(Redirect URI)。游戏网站的后端必须有一个接口来接收这个 Code。
3. 在后台发起网络请求(核心代码逻辑)
游戏网站的后端收到 Code 后,需要立即在服务器后台向微信的 Token 接口发送一个 HTTP POST 请求。拿 Python 举个例子,游戏网站后端的伪代码大致长这样:
import requests
# 1. 微信发给游戏网站的 Code
auth_code = request.get_argument("code")
# 2. 准备换取 Token 的参数
payload = {
"client_id": "游戏的ID_123456",
"client_secret": "游戏的私密密钥_abcde12345", # 核心安全:Secret 留在后端,不走浏览器
"grant_type": "authorization_code",
"code": auth_code,
"redirect_uri": "https://youxi.com"
}
# 3. 游戏网站自己发起请求,找微信换 Token
response = requests.post("https://qq.com", data=payload)
# 4. 解析拿到的 Token
tokens = response.json()
access_token = tokens.get("access_token") # 拿到了真正的令牌!
真实开发中:通常不需要从零手写
虽然逻辑要由游戏网站实现,但现代开发很少有人真的从零去拼装 HTTP 请求。
- 使用官方 SDK:微信、GitHub、Google 等大平台都会提供官方的软件开发工具包(SDK)。你只需要调用一行代码,比如
WeChatAuth.get_token(code),它在底层就会自动帮你完成拼装和发送。 - 使用通用框架:如果你用 Java(Spring Security OAuth2)或 Python(Authlib),这些成熟的框架已经把“code 换 token”的逻辑全部封装好了,你只需要在配置文件里填上
client_id和client_secret即可。
如果你想自己动手写代码接入 OAuth2 认证,可以告诉我你准备用哪个编程语言(如 Java、Python、Go)或哪个平台(如微信、GitHub、Google 登录),我可以为你提供详细的代码模板!
拿到手的是什么:JWT Token 介绍
上面换回来的 access_token,在很多平台(如微信开放平台、Google、GitHub App)其实是一串 JWT(JSON Web Token)。理解它的结构,能帮你少踩很多坑。
JWT 长什么样
JWT 是一段用两个点 . 分成三部分的字符串:Header.Payload.Signature。
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- Header(头部):声明签名算法(如
HS256/RS256)和令牌类型,是 Base64URL 编码后的 JSON。 - Payload(载荷):真正存放数据的地方,是一组“声明(Claims)”,例如用户 ID、过期时间等,同样 Base64URL 编码(只是编码,不是加密,谁都能解码看到)。
- Signature(签名):用 Header 里声明的算法 + 密钥,对前两部分算出的签名,用来防止令牌被篡改。
Payload 里常见的字段(Claims)
sub(Subject):用户唯一标识。exp(Expiration Time):过期时间戳,超过这个时间令牌就失效。iat(Issued At):签发时间。aud(Audience):受众,令牌是发给谁的。scope:这个令牌被授权能访问哪些范围。
怎么验证一个 JWT(核心安全逻辑)
游戏网站拿到 JWT 后,不能相信它就直接用,必须校验签名:
- 把字符串按
.拆成三段,前两段 Base64URL 解码得到 Header 和 Payload。 - 用相同的算法和密钥,对
Header.Payload重新计算签名。 - 把算出来的签名和令牌里的第三段比对,一致才说明没被篡改。
- 再检查
exp是否过期、aud是不是自己。
import base64, hmac, hashlib, json, time
def b64url_decode(s):
s += "=" * (-len(s) % 4)
return base64.urlsafe_b64decode(s)
def verify_jwt(token, secret):
header_b64, payload_b64, signature_b64 = token.split(".")
# 1. 重新计算签名
expected = hmac.new(secret.encode(), f"{header_b64}.{payload_b64}".encode(),
hashlib.sha256).digest()
actual = b64url_decode(signature_b64)
assert hmac.compare_digest(expected, actual), "签名不符,令牌被篡改!"
# 2. 解析并检查过期时间
payload = json.loads(b64url_decode(payload_b64))
assert payload["exp"] > time.time(), "令牌已过期!"
return payload # 校验通过,可以信任里面的用户身份
注意:用 对称密钥(HS256) 时,游戏网站和认证服务器共享同一把密钥;用 非对称密钥(RS256) 时,认证服务器用私钥签名,游戏网站只用公钥验签,更安全,也更适合多方验证。
为什么要用 JWT,而不是随便存个 session
- 自包含:令牌里自带用户信息和权限,资源服务器验签即可,不必每次查数据库。
- 跨服务:在微服务 / 多服务器架构下,任何持有公钥的服务都能独立校验,非常适合分布式系统。
- 无状态:服务端不需要保存会话,方便水平扩展。
但要注意:JWT 一旦签发,在 exp 之前都有效,无法主动吊销(除非额外维护黑名单),所以过期时间一般设得较短,配合 Refresh Token 使用。
其他授权模式
除了上面最常用的授权码模式,OAuth 2.0 还有三种应对不同场景的办法:
- 客户端凭证模式(Client Credentials):没有用户参与。是两个服务器之间互相调用数据,直接用账号密码换令牌。
- 密码模式(Password):用户非常信任客户端,直接把微信密码告诉游戏,游戏帮用户去要令牌(现在很少用,不安全)。
- 隐式模式(Implicit):没有授权码这一步,直接把令牌显示在网址上。一般用于没有后台的纯前端单页应用(现在也不推荐使用)。
Grant Types
- Authorization Code
- Implicit
- Password
- Client Credentials
- Device Code
- Refresh Token
OAuth2 vs OpenIDConnect
Reference
- https://spring.io/guides/tutorials/spring-boot-oauth2/
- https://tools.ietf.org/html/rfc6749
- https://oauth.net/2/
- https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2
- https://cloud.tencent.com/developer/article/2031455
- https://www.authgear.com/zh-hant/post/what-is-oauth-2-0-and-how-it-works/
- https://zhuanlan.zhihu.com/p/3129560220
- https://zhuanlan.zhihu.com/p/2115555401
- https://help.aliyun.com/zh/pds/drive-and-photo-service-dev/user-guide/oauth-2-0-access-process-for-web-server-applications
- https://dev.mi.com/console/doc/detail?pId=711
- https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html