5G 核心网(5GC)相对 4G EPC 最根本的架构变化,是控制面网元之间从"点到点参考点接口(S6a/S11/S5 等)"演进为服务化接口(Service Based Interface, SBI)。所有网络功能(Network Function, NF)统一以 RESTful + HTTP/2 的方式对外暴露能力,并通过网络仓储功能(NRF)完成注册、发现与调用。
这一变化带来三个 4G 时代不存在的安全问题:
nudm-sdm(签约数据),但不应该被授权调用 nudm-ee(事件暴露)。仅仅"认证你是合法 NF"不够,还必须"授权你只能调用某个具体服务"。为此,TS 33.501 Section 13 专门定义了 SBA 安全(Security aspects of the Service Based Architecture),它由三层防御体系构成:传输层安全(NDS for SBA)+ NF 注册/发现认证 + OAuth2 服务级授权。本文聚焦第三层——也是其中机制最复杂、信令交互最丰富的一层:NRF 作为 OAuth2 授权服务器,向 NF Consumer 签发 JWT 访问令牌(Access Token)的全链流程。
设计意图:3GPP 没有自己发明一套鉴权协议,而是复用成熟的 OAuth 2.0 Client Credentials 授权模式(RFC 6749),并强制令牌采用 JWT(RFC 7519)格式。这样运营商可以复用现成的 PKI/Token 中间件生态,同时令牌自包含(无需每次回查 NRF),极大降低 Nnrf 接口的鉴权信令风暴。
flowchart TB
subgraph L3["第3层:服务级授权 Authorization"]
O["OAuth2 Client Credentials<br/>NRF = Authorization Server<br/>JWT Access Token (Bearer)"]
end
subgraph L2["第2层:注册与发现认证 Authentication"]
R["mTLS 双向认证<br/>NRF 校验 NF 证书 (TS 33.310 CA)"]
end
subgraph L1["第1层:传输层保护 Transport"]
T["NDS for SBA: TLS 1.2/1.3 或 IPsec<br/>TS 33.210 / TS 33.310"]
end
NFc[NF Consumer] --> L1 --> L2 --> L3 --> NFp[NF Producer]
style L3 fill:#fde2e4
style L2 fill:#fff3bf
style L1 fill:#d3f9d8
三层职责严格分工:
下文重点拆解第 3 层。
下图展示一次完整的"AMF 想要读取 UDM 签约数据"的服务调用,从注册到令牌获取再到服务调用的完整 4 个阶段:
sequenceDiagram
autonumber
participant AMF as NF Consumer<br/>(AMF)
participant NRF as NRF<br/>(Authorization Server)
participant UDM as NF Producer<br/>(UDM)
participant CA as Operator PKI<br/>(TS 33.310)
Note over AMF,CA: 阶段0:建立 mTLS 信任(一次性)
CA-->>AMF: 签发 NF 证书
CA-->>UDM: 签发 NF 证书
CA-->>NRF: 签发 NRF 证书
Note over AMF,NRF: 阶段1:NF 注册 (Nnf_NFManagement_NFRegister)
AMF->>NRF: PUT /nnrf-nfm/v1/nf-instances/{amfId}<br/>携带 NFProfile<br/>[mTLS 双向认证]
NRF->>NRF: 校验 AMF 证书 (链→CA)<br/>存储 NFProfile
NRF-->>AMF: 201 Created
Note over AMF,NRF: 阶段2:NF 发现 (Nnf_NFDiscovery)
AMF->>NRF: GET /nnrf-disc/v1/nf-instances?<br/>target-nf-type=UDM&...<br/>[mTLS]
NRF-->>AMF: 200 OK<br/>返回 UDM 的 NFProfile (FQDN/IP, 服务列表)
Note over AMF,NRF: 阶段3:获取 OAuth2 令牌 (Nnrf_AccessToken_Get)
AMF->>NRF: POST /oauth2/token<br/>grant_type=client_credentials<br/>nfInstanceId=AMF, targetNfType=UDM<br/>scope=nudm-sdm<br/>requesterPlmn, targetPlmn<br/>[mTLS 客户端鉴权]
NRF->>NRF: 构造 JWT<br/>用 NRF 私钥签名 (ES256)
NRF-->>AMF: 200 OK<br/>{access_token:JWT, token_type:Bearer,<br/>expires_in:3600, scope}
Note over AMF,UDM: 阶段4:携带令牌调用服务 (Nudm_SDM_Get)
AMF->>UDM: GET /nudm-sdm/v2/{suppi}/am-data<br/>Authorization: Bearer {JWT}<br/>[mTLS]
UDM->>UDM: 验签(NRF公钥)→iss→aud→exp→scope
UDM-->>AMF: 200 OK<br/>AccessAndMobilitySubscriptionData
关键点:令牌的获取(阶段 3)与服务的调用(阶段 4)是解耦的。AMF 拿到令牌后会本地缓存(通常到 expires_in 前 30~60 秒),后续多次调用 UDM 都复用同一个令牌,直到过期再刷新。这正是 OAuth2 设计相对于"每次调用都回查 NRF"的核心价值。
阶段 3 的请求是 OAuth2 标准 client_credentials 模式,但 3GPP 在 RFC 6749 的基础上扩展了大量 NF 专有字段,用于 NRF 做细粒度授权决策。下面是 TS 29.510 中 AccessTokenReq 的结构:
flowchart LR
REQ["AccessTokenReq<br/>POST /oauth2/token<br/>Content-Type: x-www-form-urlencoded"]:::root
REQ --> G["grant_type<br/>=client_credentials"]:::req
REQ --> NFI["nfInstanceId<br/>发起方NF实例ID"]:::req
REQ --> SC["scope<br/>请求的服务范围"]:::req
REQ --> NT["targetNfType<br/>目标NF类型"]
REQ --> NFT["nfType<br/>发起方NF类型"]
REQ --> RPM["requesterPlmn<br/>发起方PLMN"]
REQ --> TPM["targetPlmn<br/>目标PLMN"]
REQ --> RSN["requesterSnssaiList<br/>发起方切片"]
REQ --> TSN["targetSnssaiList<br/>目标切片"]
REQ --> TNFI["targetNfInstanceId<br/>精确目标实例"]
REQ --> TSET["targetNfSetId<br/>目标NF集合"]
classDef req fill:#ffe066,stroke:#333
classDef root fill:#a5d8ff,stroke:#333
下表对每个 IE 进行说明:
| IE 字段 | 是否必选 | 含义与设计意图 | 示例值 |
|---|---|---|---|
grant_type |
必选 | 固定为 client_credentials。SBA 中 NF 调用 NF 是"机器对机器",没有用户参与,故只能用此模式,不能用授权码模式。 |
client_credentials |
nfInstanceId |
必选 | 发起方 NF 的全局唯一实例 ID(UUID),对应阶段 1 注册时的 nfInstanceId,是 JWT 中 sub 的来源。 |
550e8400-e29b-... |
scope |
必选 | 请求授权的服务名集合,格式 <service-name> [target-nf-type] [requester-nf-type],是后续授权裁决的核心依据。 |
nudm-sdm |
targetNfType |
可选 | 目标 NF 类型(UDM/PCF/UDR…),用于限定令牌受众,落入 JWT 的 aud。 |
UDM |
nfType |
可选 | 发起方 NF 类型,便于 NRF 按角色裁剪授权(例如限制 RAN 侧 NF 不能调用计费服务)。 | AMF |
requesterPlmn / targetPlmn |
可选 | 漫游场景关键字段。当跨 PLMN 调用经 SEPP 时,NRF 依据 PLMN 颁发定向令牌,防止令牌被跨运营商滥用。 | 46000 / 46001 |
requesterSnssaiList / targetSnssaiList |
可选 | 网络切片维度授权,使令牌仅在特定切片内有效,实现切片级隔离。 | 01-000001 |
targetNfInstanceId / targetNfSetId |
可选 | 将令牌绑定到特定实例或 NF 集合,缩小令牌泄露后的爆炸半径。 | set-id |
设计意图:RFC 6749 的原始
client_credentials只有grant_type、scope、客户端凭证。3GPP 通过加入targetNfType、requesterPlmn、requesterSnssaiList等,把一个"粗粒度的应用令牌"升级为"绑定到目标网元+运营商+切片的多维令牌",从而在 JWT 的 claim 里把后续服务调用所需的全部上下文一次性固化,使 Producer 端的本地校验成为可能(无需回调 NRF),这是 5GC 能支撑百万级 SBI 调用/秒的关键。
NRF 返回的 AccessTokenRsp 非常简洁(符合 RFC 6749):
{
"access_token": "<JWT 字符串>",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "nudm-sdm"
}
真正有技术含量的是 access_token 内部的 JWT(JSON Web Token)。TS 29.510 Annex C 规定了其 claim 结构,由 NRF 用自己的私钥签名(推荐 ES256 / RS256):
flowchart TB
JWT["JWT = Header.Payload.Signature"]:::jwt
JWT --> H["Header<br/>{alg:ES256, typ:JWT}"]
JWT --> P["Payload (Claims)"]:::claim
JWT --> S["Signature<br/>ES256(Header.Payload, NRF私钥)"]:::sig
P --> C1["iss: NRF 的 FQDN"]
P --> C2["sub: 发起方 NF Instance ID"]
P --> C3["aud: 目标 NF 类型/实例"]
P --> C4["exp: 过期时间(Unix秒)"]
P --> C5["iat: 签发时间"]
P --> C6["scope: 实际授予范围<br/>≤请求scope"]
P --> C7["jti: 令牌唯一ID<br/>(防重放/可吊销)"]
P --> C8["nfInstanceId / targetNfInstanceId<br/>+ 自定义 claim"]
classDef jwt fill:#b197fc
classDef claim fill:#ffc9c9
classDef sig fill:#bac8ff
JWT 自包含的好处:UDM 在阶段 4 校验令牌时,只需要 NRF 的公钥证书(一次性获取并缓存),就能在本地完成验签 + 过期检查 + scope 检查,全过程不产生到 NRF 的任何反向信令。对比 Diameter 时代每次鉴权都要回 HSS 查询,这是一个数量级的性能提升。
一个 access token 从签发到被废弃,经历如下状态流转。理解这张图是排错的基础:
stateDiagram-v2
[*] --> Issuing: POST /oauth2/token
Issuing --> Valid: 200 OK (exp=T+3600s)
Valid --> InUse: 携带 Bearer 调用 Producer
InUse --> Valid: 本地缓存复用
Valid --> Refreshing: 距 exp < 提前量(如60s)
Refreshing --> Valid: 申请新令牌
Valid --> Expired: 当前时间 ≥ exp
Expired --> [*]: Producer 返回 401<br/>触发重新获取
InUse --> Revoked: NRF 吊销(jti 入黑名单)
Revoked --> [*]: Producer 校验失败
note right of Valid
Producer 端 4 项校验:
1. 验签(NRF公钥)
2. iss==NRF
3. aud 匹配本 NF 类型
4. scope 覆盖被调服务
end note
注意 3GPP 的实现中通常不支持令牌主动吊销(revocation endpoint 可选),主要依赖短有效期(默认 1 小时)自然过期。这也是为什么 expires_in 一般设置为 3600 秒而非 24 小时——以时间换安全。
阶段 1~4 的每一次 HTTP/2 报文,都必须承载在受保护通道上。TS 33.501 §13 与 TS 33.210 §6.2 共同定义了 NDS for SBA:
| 维度 | PLMN 内部(Intra-PLMN) | 跨 PLMN 漫游(Inter-PLMN) |
|---|---|---|
| 保护位置 | NF 之间 / NF 与 SCP 之间 | 经由 SEPP 在 N32 接口 |
| 机制 | mTLS 双向认证(TLS 1.2/1.3) | TLS VPN 互信 + SEPP 应用层映射(PRINS) |
| 证书来源 | 运营商私有 CA(TS 33.310) | 运营商间互信 CA / 测试证书 |
| 对 OAuth2 的影响 | mTLS 同时充当 OAuth2 的客户端鉴权(无需在 body 传 client_secret) | 令牌中 requesterPlmn/targetPlmn 生效 |
| 关键规范 | TS 33.210 §6.2, TS 33.310 | TS 33.501 §13.1.2, TS 33.512(NDS for N32) |
关键细节:在
client_credentials模式下,RFC 6749 要求客户端证明身份(通常用client_secret)。但在 SBA 中,由于底层已是 mTLS,NRF 直接把 mTLS 客户端证书中的身份当作 OAuth2 客户端身份,无需在/oauth2/token请求里明文传client_secret。这就是为什么前文AccessTokenReq里没有client_secret字段——它由传输层承担。这是 3GPP 对标准 OAuth2 的一个安全增强。
由于 SBI 跑在 HTTP/2 + TLS 上,要在 NF 之间抓到明文,必须在 NF 进程内启用 SSLKEYLOGFILE(开源 free5GC、OAI 均支持),否则只能看到 TLS 加密流量。识别 OAuth2 流量的过滤条件:
# 令牌申请请求(阶段3)
http2.headers.method == "POST" && http2.headers.path contains "/oauth2/token"
http2.headers.content_type == "application/x-www-form-urlencoded"
# 令牌响应
http2.headers.content_type == "application/json" && frame contains "access_token"
# 服务调用(阶段4)—— 关键是 Authorization: Bearer
http2.headers.authorization contains "Bearer"
一个典型故障场景的抓包特征:阶段 3 成功返回 JWT,但阶段 4 Producer 回 401 Unauthorized——这几乎 100% 是令牌校验失败(见下文)。
| 症状 | 可能根因 | 排查方向 |
|---|---|---|
POST /oauth2/token 返回 401 |
mTLS 客户端证书无效/过期/CA 不被 NRF 信任 | 检查 NF 证书有效期、CA 链、NRF 信任库(TS 33.310) |
返回 400 + invalid_scope |
请求的 scope 与 NF 角色不匹配,或服务名拼写错误 |
核对 TS 29.5xx 中该服务的标准 scope 名(如 nudm-sdm,不是 udm-sdm) |
Producer 返回 401 |
JWT 验签失败(NRF 公钥未更新)/ exp 已过 / iss 不匹配 |
在 Producer 抓包解码 JWT Header+Payload;确认 Producer 缓存的 NRF 公钥与签发密钥配对 |
Producer 返回 403 |
认证通过但授权不通过:scope 未覆盖被调服务,或 aud 与本 NF 类型不符 |
检查 JWT 的 scope、aud claim;是否 AMF 拿了 nudm-sdm 的令牌却去调 nudm-ueau |
| 漫游场景间歇性 403 | requesterPlmn/targetPlmn 未携带或 SEPP 透传后被改写 |
确认漫游双方 PLMN ID 配置一致,SEPP 应用层策略 |
| ## 九、与其他方案的对比 |
| 维度 | OAuth2 Access Token(SBA, 本方案) | 传统 Diameter 鉴权(EPC) | API Key / 静态密钥 |
|---|---|---|---|
| 标准基础 | RFC 6749 + RFC 7519(JWT) | RFC 6733 | 厂商私有 |
| 粒度 | 服务级(scope)+ 网元级(aud)+ 切片级(S-NSSAI) | 接口级 | 仅"是否合法" |
| 鉴权位置 | Producer 本地(JWT 自包含) | 需回查 HSS/ Diameter 代理 | Producer 本地比对 |
| 过期/吊销 | 短时过期 + 可选 revocation | 长连接 + 会话 | 无 |
| 横向扩展 | 天然无状态,易 Scale-out | 有状态,Diameter 代理压力大 | 无状态 |
| 适用场景 | 大规模云原生 NF 间调用 | 4G 网元间 | 简单内部系统 |
可以看出,SBA OAuth2 是为云原生、服务网格化、高并发量身定制的方案,这也是它在 Rel-15 引入后迅速成为 5GC NF 间默认安全机制的原因。
回过头看整个 SBA 安全体系,可以提炼出三条贯穿始终的设计哲学:
对协议开发工程师而言,理解 SBA 安全不是孤立的"加个证书",而是一套端到端的、与架构同呼吸的信任传递体系——这正是它区别于传统网络安全、也是本文聚焦拆解它的价值所在。
Q1: 为什么 3GPP 用 client_credentials 模式,而不是授权码(authorization code)模式?
A: 授权码模式是为"有用户参与的第三方应用授权"设计的(如用微信登录第三方 App)。而 SBA 中是 NF 调用 NF,没有用户参与,发起方就是机器本身,使用 client_credentials 模式可以让 NF 直接用自身身份(mTLS 证书)换取令牌,流程最简、最契合 M2M 场景。
Q2: 既然底层已有 mTLS,为什么还要再加一层 OAuth2 令牌?这不是冗余吗?
A: 不冗余,二者解决不同问题。mTLS 解决"你是谁"(认证),OAuth2 解决"你能做什么"(授权)。mTLS 只能证明 AMF 是合法 NF,但无法表达"AMF 可以调 nudm-sdm 但不能调 nudm-ee"这种服务级、参数级细粒度策略。令牌的 scope + aud + 切片 claim 才是实现最小权限原则的关键。
Q3: JWT 令牌过期后,正在进行的批量服务调用会怎样?
A: Producer 校验到 exp 已过会返回 401 Unauthorized。健壮的 Consumer 实现会捕获 401 → 自动重新执行 Nnrf_AccessToken_Get → 用新令牌重试,对上层业务透明。因此实践中建议 Consumer 在本地令牌剩余有效期低于阈值(如 60s)时就主动预刷新,避免请求期内的 401 抖动。
Q4: 令牌丢失或泄露怎么办?SBA 支持令牌吊销吗?
A: TS 29.510 中 revocation 是可选实现,多数商用部署依赖短有效期(默认 3600s)自然过期来控制风险,而非主动吊销。如果需要更强保障,可结合 jti(令牌唯一 ID)+ NRF 黑名单,或通过收紧 aud/targetNfInstanceId 把令牌绑定到特定实例,缩小泄露爆炸半径。
Q5: 引入了 SCP(服务通信代理)后,令牌该由谁申请? A: TS 29.500 定义了多种通信模型。在 Model C(委托授权) 下,Consumer 不自己申请令牌,而是把请求发给 SCP,由 SCP 代为向 NRF 申请令牌并附加到请求中转发给 Producer。这种模式下 Consumer 完全无感,但调试时需在 SCP 侧抓 OAuth2 信令。
/oauth2/token 信令与抓包细节本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
