当前位置: 首页 > news >正文

微服务中使用JWT 认证体系详解:HMAC vs RSA 签名、OAuth2 Token 获取机制

微服务中使用JWT 认证体系详解:HMAC vs RSA 签名、OAuth2 Token 获取机制

一、JWT 基础概念

1.1 什么是 JWT

JWT(JSON Web Token)是一种紧凑的、自包含的令牌格式,用于在各方之间安全传递信息。它由三部分组成,用点号.分隔:

xxxxx.yyyyy.zzzzz ↓ ↓ ↓ Header Payload Signature
  • Header:声明令牌类型和签名算法
  • Payload:存放实际数据(用户ID、权限、过期时间等)
  • Signature:对前两部分的签名,防止数据被篡改

1.2 一个 JWT 长什么样

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ0ZXN0IiwiZXhwIjoxNzg0Njc4NDA1fQ.签名部分

对 Header 进行 Base64 解码:

{"alg":"RS256",// 签名算法"typ":"JWT"// 令牌类型}

对 Payload 进行 Base64 解码:

{"user_name":"test","member_id":100000,"exp":1784678405,// 过期时间(Unix 时间戳)"client_id":"my_app","scope":["read","write"]}

注意:Header 和 Payload 只是 Base64 编码(不是加密),任何人都能解码看到内容。Signature 才是保证数据不被篡改的关键。


注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

二、两种签名算法对比:HMAC vs RSA

2.1 HMAC(对称加密签名)

代表算法:HS256、HS384、HS512

原理:用一个共享密钥(Secret Key)对数据进行签名和验签,签名方和验签方使用同一个密钥

签名过程:HMAC-SHA256(Header + "." + Payload, 密钥) → Signature 验签过程:用同一个密钥重新计算签名,对比是否一致

特点

  • 签名和验证都用同一把钥匙
  • 速度快、实现简单
  • 适合单体系统或前端-后端之间的简单场景
  • 密钥泄露 = 既能伪造又能验证

典型场景:前端登录后,后端用一个 Secret 签发 Token,前端携带该 Token 访问同一后端。

┌──────────┐ 同一把密钥 ┌──────────┐ │ 登录服务 │ ←────────────────────→ │ 业务服务 │ │ (签发方) │ 签发 + 验证都用它 │ (验证方) │ └──────────┘ └──────────┘

2.2 RSA(非对称加密签名)

代表算法:RS256、RS384、RS512

原理:使用一对公钥-私钥。私钥签名,公钥验签。签名方和验签方使用不同的密钥

签名过程:RSA-SHA256(Header + "." + Payload, 私钥) → Signature 验签过程:用公钥验证签名是否与数据匹配

特点

  • 私钥签名(只有认证服务有)
  • 公钥验签(所有业务服务都可以有)
  • 公钥泄露无所谓,只能验证不能伪造
  • 适合分布式微服务架构
  • 计算稍慢

典型场景:独立的认证服务用私钥签发 Token,各个微服务用公钥验证 Token 的合法性。

┌──────────────┐ │ 认证服务 │ 持有私钥,负责签发 Token │ (Auth Server)│ └──────┬───────┘ │ 签发的 Token ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ 服务 A │ │ 服务 B │ │ 服务 C │ │ 持有公钥 │ │ 持有公钥 │ │ 持有公钥 │ │ 只能验证 │ │ 只能验证 │ │ 只能验证 │ └──────────┘ └──────────┘ └──────────┘

2.3 核心区别一览

维度HMAC (HS256)RSA (RS256)
密钥一个共享密钥一对公私钥
签发持有密钥的任何人只有持有私钥的一方
验证持有密钥的任何人持有公钥的任何人
安全性密钥泄露 = 全部完蛋公钥公开也安全
性能稍慢
适用场景单体/简单前后端微服务/多系统
密钥管理所有需验证的服务都要密钥只有认证服务有私钥

三、为什么微服务架构用 RSA 而不是 HMAC

假设你有 20 个微服务都需要验证 Token:

如果用 HMAC

  • 每个服务都要存储同一个密钥
  • 任何一个服务泄露密钥,攻击者就能伪造 Token
  • 密钥轮换时要同时更新 20 个服务

如果用 RSA

  • 只有认证服务有私钥
  • 其他 20 个服务只有公钥,泄露了也只能验证不能伪造
  • 私钥轮换只改认证服务一处

这就是为什么项目使用独立的认证服务+ RSA 公钥的架构。


四、OAuth2 协议基础

4.1 OAuth2 是什么

OAuth2 是一套授权框架,定义了客户端如何获取访问令牌(Access Token)。它不关心 Token 的具体格式,但通常与 JWT 配合使用。

4.2 四种授权模式

模式适用场景简述
Authorization CodeWeb应用、第三方登录最安全,需要浏览器跳转
Password Grant内部系统、受信任客户端直接传账号密码换 Token
Client Credentials服务间调用无用户参与,纯机器对机器
Implicit已废弃不推荐使用

4.3 Password Grant 详解

客户端(ApiPost/curl) 认证服务(Auth Server) │ │ │ POST /oauth/token │ │ Authorization: Basic base64(id:pw)│ │ Body: grant_type=password │ │ &username=xxx │ │ &password=xxx │ │──────────────────────────────────→ │ │ │ 验证账号密码 │ │ 用私钥签发 JWT │ { │ │ "access_token": "eyJ...", │ │ "token_type": "bearer", │ │ "expires_in": 53640, │ │ "refresh_token": "eyJ..." │ │ } │ │←────────────────────────────────── │ │ │

请求中的两层认证

  1. Basic AuthAuthorization: Basic xxx):证明"你是哪个客户端应用"
    • client_id:client_password进行 Base64 编码
    • 例如:demo_app:123456ZGVtb19hcHA6MTIzNDU2
    • 表示"我是 demo_app 这个应用"
  2. Body 中的账号密码:证明"你代表哪个用户"
    • username+password
    • 表示"我代表 zhangsan 这个用户请求授权"

4.4 返回结果字段说明

{"access_token":"eyJhbGciOiJSUzI1NiIs...",// 访问令牌,调接口时用"token_type":"bearer",// 令牌类型,固定为 bearer"refresh_token":"eyJhbGciOiJSUzI1NiIs...",// 刷新令牌,用于续期"expires_in":53640,// access_token 有效期(秒)"scope":"read write",// 授权范围"user_name":"zhangsan",// 用户名"member_id":100000// 业务字段}

五、两种 Token 的本质区别

以实际场景说明为什么"前端登录拿到的 Token"不能用于调用微服务接口:

5.1 前端 Web Token(HMAC 签名)

浏览器 → 前端登录接口(/open/login) → 返回 coc_jwt(放在 Set-Cookie 中)
  • 签名算法:HS512(HMAC)
  • 签发方:前端网关/BFF 层
  • 验证方:同一个前端网关
  • 用途:标识浏览器会话,前端页面鉴权
  • 特征:密钥只在网关内部,微服务拿不到这个密钥

5.2 微服务 Token(RSA 签名)

客户端 → 认证服务(/oauth/token) → 返回 access_token(JWT,RS256 签名)
  • 签名算法:RS256(RSA)
  • 签发方:专用认证服务(用私钥签名)
  • 验证方:所有微服务(用公钥验签)
  • 用途:服务间调用鉴权
  • 特征:每个微服务配置公钥即可独立验证

5.3 为什么不能混用

前端 Token (HS512) → 微服务 (配置的是 RSA 公钥) ↓ 尝试用 RSA 公钥验证 HMAC 签名 ↓ ❌ "Cannot convert access token to JSON"

微服务只认识 RSA 签名的 Token。你拿 HMAC 签名的 Token 给它,它用 RSA 公钥去验签,格式完全对不上,所以报错。


六、完整认证架构图

┌─────────────────────────────┐ │ 认证服务 (Auth Server) │ │ 持有 RSA 私钥 │ │ /oauth/token │ │ 签发 RS256 JWT │ └──────────┬──────────────────┘ │ ┌────────────────┼────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ A 服务 │ │ B 服务 │ │ C 服务 │ │ 端口 3012 │ │ 端口 3013 │ │ 端口 3014 │ │ 配置 RSA 公钥│ │ 配置 RSA 公钥│ │ 配置 RSA 公钥│ │ 独立验签 │ │ 独立验签 │ │ 独立验签 │ └──────────────┘ └──────────────┘ └──────────────┘ ┌─────────────────────────────────────────────────┐ │ 前端网关 (BFF / Gateway) │ │ /open/login → 签发 coc_jwt (HS512) │ │ 用于浏览器 Cookie 会话管理 │ │ 与微服务 Token 体系完全独立 │ └─────────────────────────────────────────────────┘

七、通用代码示例

7.1 Java Spring Boot 配置 RSA 公钥验签

# application.ymlsecurity:jwt:signing-key:|-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... -----END PUBLIC KEY-----resource-ids:my-servicematchers:-path:/api/public/**attribute:permitAll-path:/api/**attribute:authenticated

7.2 curl 获取 OAuth2 Token

# 将 client_id:client_password 进行 Base64 编码后放入 Basic Authcurl-s-XPOST"https://auth-server.com/oauth/token"\-H"Content-Type: application/x-www-form-urlencoded"\-u"my_client_id:my_client_password"\-d"grant_type=password&username=user&password=pass"

-u参数等价于:

-H"Authorization: Basic$(echo-n'my_client_id:my_client_password'|base64)"

7.3 PowerShell 获取 Token

# 构造 Basic Auth$pair="my_client_id:my_client_password"$base64=[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($pair))# 请求 Token$headers= @{"Authorization"="Basic$base64""Content-Type"="application/x-www-form-urlencoded"}$body="grant_type=password&username=user&password=pass"$response=Invoke-WebRequest-Uri"https://auth-server.com/oauth/token"`-Method POST-Headers$headers-Body$body-UseBasicParsing$token=($response.Content|ConvertFrom-Json).access_token

7.4 Java 代码获取 Token(RestTemplate)

/** * 通过 OAuth2 Password Grant 获取访问令牌 */publicStringgetAccessToken(){StringclientId="my_client_id";StringclientPassword="my_client_password";Stringcredentials=Base64.getEncoder().encodeToString((clientId+":"+clientPassword).getBytes());HttpHeadersheaders=newHttpHeaders();headers.set("Authorization","Basic "+credentials);headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);MultiValueMap<String,String>body=newLinkedMultiValueMap<>();body.add("grant_type","password");body.add("username","user");body.add("password","pass");HttpEntity<MultiValueMap<String,String>>request=newHttpEntity<>(body,headers);ResponseEntity<Map>response=restTemplate.postForEntity("https://auth-server.com/oauth/token",request,Map.class);return(String)response.getBody().get("access_token");}

7.5 使用 Token 调用业务接口

# 获取到 token 后curl-s-XPOST"http://127.0.0.1:8080/api/your-endpoint"\-H"Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."\-H"Content-Type: application/json"\-d'{"pageNum":1,"pageSize":10}'

八、验签过程详解

当业务服务收到请求时:

1. 从 Header 中提取: Authorization: Bearer eyJxxx.eyJyyy.zzz 2. 拆分 JWT 三段: - Header: eyJxxx → 解码得 {"alg":"RS256","typ":"JWT"} - Payload: eyJyyy → 解码得 {"user_name":"test","exp":1784678405,...} - Signature: zzz 3. 用配置的 RSA 公钥验证签名: RSA_Verify(公钥, "eyJxxx.eyJyyy", zzz) → true/false 4. 如果签名验证通过: - 检查 exp 是否过期 - 检查 resource-ids 是否匹配 - 提取用户信息注入到 SecurityContext 5. 如果验证失败: - 返回 401 Unauthorized - 错误信息如 "Cannot convert access token to JSON"(格式不对) - 或 "Access token expired"(过期)

九、总结

概念一句话解释
JWT一种自包含的令牌格式,三段式结构
HMAC (HS256/512)对称签名,一把钥匙既签又验
RSA (RS256)非对称签名,私钥签、公钥验
OAuth2一套获取令牌的标准流程/协议
Password GrantOAuth2 的一种模式,直接用账号密码换 Token
Basic Auth用 Base64(id:password) 标识客户端身份
Bearer TokenToken 的传递方式,放在 Authorization Header 中
公钥公开的,只能验证签名,不能伪造
私钥保密的,用来签名,只有认证服务持有

核心理解:在微服务架构中,认证服务是唯一持有私钥的"发证机关",各个业务服务通过公钥验证"证件"的真伪,而不需要每个服务都能"发证"。前端页面用的是另一套简单的会话机制(HMAC Cookie),两者是独立的认证体系。

http://www.jsqmd.com/news/1244615/

相关文章:

  • 徐州智能阳台升降窗生产厂家价格及实用选购指南 - 品牌优推
  • 百亿级系统架构设计实录:Java架构师开发必备!
  • 鸿蒙 ArkTS 实战:Course GPA Calculator 从课程绩点计算到成绩统计应用完整解析
  • 找专业音响安装厂商,真实安装体验和效果究竟咋样?
  • 【2024网络运维生死线】:AI实时流分析工具如何将MTTR从47分钟压缩至8.3秒?
  • 天津劳动工伤律师精选:祁松律师执业8年实战派维权 - 本地品牌推荐
  • 河南移动宽带测速
  • 2026 年 7 月新发布:新郑比较好的天然泰山石工厂综合实力解析,揭秘泰山石的秘密:它如何颠覆你的家居审美?-界石景观石 - 行业推荐官[官方】--
  • 当失业的程序员想转行去做产品经理
  • 中翰软件:用“本体论”给数据治理立规矩,用AI让规矩“活”起来
  • 2026年天津GEO优化公司哪家好 正规排行参考 - 起跑123
  • 北京市专精特新中小企业认定常见问题解答
  • 鸿蒙 ArkTS 实战:晨间清单板的时间线、天气同步与出门卡片设计
  • 2026宁波口碑好的门窗加工厂家评测汇总推荐 - 起跑123
  • 2026年江苏针孔收缩膜实用选购参考及适配方案指引 - 品牌优推
  • 2026长沙全屋定制厂家哪家好 正规优质服务商盘点推荐 - 起跑123
  • 【Java EE】Spring 日志详解
  • AI Agent开发指南:从基础认知到实战应用
  • 江西实木门楼厂核心工艺解析及本地项目落地实用指南 - 品牌优推
  • 2026年TGS高效双吸泵怎么安装及配套服务商选择指南 - 热点品牌推荐
  • 2026年云南开餐饮店选烹饪设备供应商哪家靠谱攻略 - 热点品牌推荐
  • 2026山东综评培训机构哪家好 选报参考维度全梳理 - 起跑123
  • DolphinX 实测:当 AI Agent 真正读懂你的时序数据,事情开始变得不一样
  • 含多类型V2G电动汽车的微网日前-日内两阶段协同优化调度模型研究(Matlab代码实现)
  • 劳力士服务项目及价格查询|维修地址与售后服务电话权威信息通知(2026年7月最新) - 劳力士服务中心
  • 鸿蒙 ArkTS 入门实战:校园午餐均衡记录的首屏状态与点击反馈
  • 鸿蒙 ArkTS 入门实战:清洁分区计时器的页面入口与状态切换
  • 鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
  • LightGlue:基于Transformer的高效特征匹配技术解析
  • 2026年四川大模型算法备案代办优质公司挑选指南 - 热点品牌推荐