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

📅 发布时间:2026/7/23 0:53:34
微服务中使用JWT 认证体系详解:HMAC vs RSA 签名、OAuth2 Token 获取机制 微服务中使用JWT 认证体系详解HMAC vs RSA 签名、OAuth2 Token 获取机制一、JWT 基础概念1.1 什么是 JWTJWTJSON Web Token是一种紧凑的、自包含的令牌格式用于在各方之间安全传递信息。它由三部分组成用点号.分隔xxxxx.yyyyy.zzzzz ↓ ↓ ↓ Header Payload SignatureHeader声明令牌类型和签名算法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 RSA2.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内部系统、受信任客户端直接传账号密码换 TokenClient Credentials服务间调用无用户参与纯机器对机器Implicit已废弃不推荐使用4.3 Password Grant 详解客户端(ApiPost/curl) 认证服务(Auth Server) │ │ │ POST /oauth/token │ │ Authorization: Basic base64(id:pw)│ │ Body: grant_typepassword │ │ usernamexxx │ │ passwordxxx │ │──────────────────────────────────→ │ │ │ 验证账号密码 │ │ 用私钥签发 JWT │ { │ │ access_token: eyJ..., │ │ token_type: bearer, │ │ expires_in: 53640, │ │ refresh_token: eyJ... │ │ } │ │←────────────────────────────────── │ │ │请求中的两层认证Basic AuthAuthorization: Basic xxx证明你是哪个客户端应用将client_id:client_password进行 Base64 编码例如demo_app:123456→ZGVtb19hcHA6MTIzNDU2表示我是 demo_app 这个应用Body 中的账号密码证明你代表哪个用户usernamepassword表示我代表 zhangsan 这个用户请求授权4.4 返回结果字段说明{access_token:eyJhbGciOiJSUzI1NiIs...,// 访问令牌调接口时用token_type:bearer,// 令牌类型固定为 bearerrefresh_token:eyJhbGciOiJSUzI1NiIs...,// 刷新令牌用于续期expires_in:53640,// access_token 有效期秒scope:read write,// 授权范围user_name:zhangsan,// 用户名member_id:100000// 业务字段}五、两种 Token 的本质区别以实际场景说明为什么前端登录拿到的 Token不能用于调用微服务接口5.1 前端 Web TokenHMAC 签名浏览器 → 前端登录接口(/open/login) → 返回 coc_jwt放在 Set-Cookie 中签名算法HS512HMAC签发方前端网关/BFF 层验证方同一个前端网关用途标识浏览器会话前端页面鉴权特征密钥只在网关内部微服务拿不到这个密钥5.2 微服务 TokenRSA 签名客户端 → 认证服务(/oauth/token) → 返回 access_tokenJWTRS256 签名签名算法RS256RSA签发方专用认证服务用私钥签名验证方所有微服务用公钥验签用途服务间调用鉴权特征每个微服务配置公钥即可独立验证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:authenticated7.2 curl 获取 OAuth2 Token# 将 client_id:client_password 进行 Base64 编码后放入 Basic Authcurl-s-XPOSThttps://auth-server.com/oauth/token\-HContent-Type: application/x-www-form-urlencoded\-umy_client_id:my_client_password\-dgrant_typepasswordusernameuserpasswordpass-u参数等价于-HAuthorization: Basic$(echo-nmy_client_id:my_client_password|base64)7.3 PowerShell 获取 Token# 构造 Basic Auth$pairmy_client_id:my_client_password$base64[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($pair))# 请求 Token$headers {AuthorizationBasic$base64Content-Typeapplication/x-www-form-urlencoded}$bodygrant_typepasswordusernameuserpasswordpass$responseInvoke-WebRequest-Urihttps://auth-server.com/oauth/token-Method POST-Headers$headers-Body$body-UseBasicParsing$token($response.Content|ConvertFrom-Json).access_token7.4 Java 代码获取 TokenRestTemplate/** * 通过 OAuth2 Password Grant 获取访问令牌 */publicStringgetAccessToken(){StringclientIdmy_client_id;StringclientPasswordmy_client_password;StringcredentialsBase64.getEncoder().encodeToString((clientId:clientPassword).getBytes());HttpHeadersheadersnewHttpHeaders();headers.set(Authorization,Basic credentials);headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);MultiValueMapString,StringbodynewLinkedMultiValueMap();body.add(grant_type,password);body.add(username,user);body.add(password,pass);HttpEntityMultiValueMapString,StringrequestnewHttpEntity(body,headers);ResponseEntityMapresponserestTemplate.postForEntity(https://auth-server.com/oauth/token,request,Map.class);return(String)response.getBody().get(access_token);}7.5 使用 Token 调用业务接口# 获取到 token 后curl-s-XPOSThttp://127.0.0.1:8080/api/your-endpoint\-HAuthorization: Bearer eyJhbGciOiJSUzI1NiIs...\-HContent-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 的一种模式直接用账号密码换 TokenBasic Auth用 Base64(id:password) 标识客户端身份Bearer TokenToken 的传递方式放在 Authorization Header 中公钥公开的只能验证签名不能伪造私钥保密的用来签名只有认证服务持有核心理解在微服务架构中认证服务是唯一持有私钥的发证机关各个业务服务通过公钥验证证件的真伪而不需要每个服务都能发证。前端页面用的是另一套简单的会话机制HMAC Cookie两者是独立的认证体系。