← [ BACK_TO_NOTES ]

Go 认证与 JWT

Go 服务中使用 JWT 作为短期 Access Token 的实现要点:Claims、验签、中间件身份注入,以及与 Session/Refresh 生命周期的边界。

#concept #growing #backend #go

[!info] related notes

Go 认证与 JWT

一句话定义

在 Go Web 服务中,JWT 常被用作短生命周期 Access Token:Middleware 验证 Token 后,把已经验证的 principal 写进 context.Context,业务层再执行资源级 Authorization。

这里的关键是:

JWT
= Token 格式

Access Token
= 凭证角色

Session / Grant
= 可选的长期登录状态

所以“Go 使用 JWT”不等于“整个认证系统必然无状态”。

一个典型 Hybrid 结构

Login
→ verify password
→ create Session / Grant
→ bind Refresh Token
→ issue short-lived Access JWT

Normal API
→ Go Middleware local verify Access JWT
→ principal into context
→ Service authorization

Refresh / Logout / Revoke
→ Auth service
→ check Session / Grant

如果系统是普通 Browser Web App,也可以直接使用 opaque Session Cookie,而完全不需要 JWT。

Access JWT 的最小 Claims

常见 Registered Claims:

sub  → subject / user identity
exp  → expiry
iat  → issued at
iss  → issuer
aud  → audience

业务 claims 应保持最小。Role/scope 可以作为短期授权上下文,但它们仍然只是签发时的快照,不能替代当前资源 ownership / policy 查询。

Go 中的验签边界

使用 github.com/golang-jwt/jwt/v5 等成熟库时,中间件通常负责:

  1. 从 Authorization: Bearer ... 提取 Access Token;
  2. 限制允许的签名算法;
  3. 验证签名;
  4. 验证 exp、iss、aud 等必要 claims;
  5. 把 sub 等已验证身份放入 request context;
  6. 调用后续 handler。

不要让 JWT Middleware 承担所有业务 Authorization。

Context 注入

推荐使用私有类型作为 key:

type ctxKey string
const userIDKey ctxKey = "user_id"

ctx := context.WithValue(r.Context(), userIDKey, claims.Subject)
next.ServeHTTP(w, r.WithContext(ctx))

下游只能把这份值当作“已经通过认证得到的 principal”,而不是客户端可自由提交的 user_id。

JWT 安全边界

Payload 不是秘密

JWT Payload 通常只经过 Base64URL 编码,可以被读取;密码、密钥等敏感秘密不要放进去。

必须限制算法

不要盲信 Token Header 中的 alg。服务端要显式限制允许的算法,避免算法混淆或错误配置。

Access Token 应控制有效窗口

长寿命 Access Token 会让账户封禁、权限变化和 Session 撤销更晚生效。短 TTL 用来限制 stale authorization window;具体是 5、15 或 60 分钟取决于威胁模型和刷新成本,没有固定标准。

Refresh Token

Refresh Token 不需要是 JWT。更常见的安全模型可以是:

opaque random Refresh Token
→ hash lookup
→ Session / Grant
→ rotate
→ issue new Access JWT

Refresh Token 的 rotation、replay detection 和撤销属于长期登录生命周期,不应塞进普通 JWT Middleware。

Pure Stateful 与 Hybrid

Pure Session

request
→ opaque Session Token
→ shared Session Store

即时撤销强,但每请求查询共享状态。

Hybrid

normal request
→ local Access JWT verify

refresh/logout/revoke
→ server-side Session / Grant

高频路径减少共享状态查询,但接受 Access Token 在 exp 前存在有限滞后窗口。

Authentication 与 Authorization

JWT 验证成功只建立身份上下文:

sub = user-42
scope = assessment:write

业务层仍要判断:

user-42 是否拥有 Assessment 789?
当前 tenant membership 是否有效?
资源是否处于允许修改的状态?

因此不要把 role 塞进 JWT 后就认为 Authorization 问题已经解决。

常见误区

  • “JWT 认证天然无状态” → 取决于系统是否还有 Session/Grant/revocation 状态。
  • “JWT 是微服务默认最佳方案” → 要看服务边界和撤销需求。
  • “Refresh Token 必须 JWT” → 不需要。
  • “JWT 验证成功就可以操作任意资源” → 仍需资源级授权。
  • “退出登录只要客户端删除 JWT” → Hybrid/Session 架构还应撤销服务端长期状态。

Go 工程实践

  • 密钥从 Secret Manager / 环境注入,不硬编码;
  • 支持密钥轮换,非对称场景可使用 kid;
  • 不记录完整 bearer token;
  • 给过期、错误算法、错误 audience/issuer 写测试;
  • Middleware 只建立 principal,业务 Policy 放 Service/Authorization 层;
  • 多实例系统明确 Session/Refresh authority 放在哪里。

最短记忆

Go 的 JWT Middleware 负责验证短期凭证并注入身份;长期登录生命周期是否有状态、如何 Refresh 和 Revoke,是更上一层的认证架构决策。

创建于 2026/6/25 更新于 2026/9/30
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本