如何在 JWT 中嵌入并提取 RSA 公钥(使用 jwt-go 实现)
发布时间 - 2026-01-31 00:00:00 点击率:次本文详解如何安全地将 rsa 公钥以 pem 字符串形式嵌入 jwt 的 claims 中,并在解析时动态还原为 *rsa.publickey,实现基于公钥分发的灵活验签逻辑。适用于多租户、密钥轮换或去中心化签名验证场景。
在标准 JWT 实践中,签名验证依赖服务端预置的公钥(如 jwt.Parse(..., func() { return publicKey })),但该方式缺乏灵活性:无法支持动态密钥切换、多租户独立密钥,或客户端自主选择验证方。一种常见误解是直接将 *big.Int 类型的 N/E 字段存入 claims——这不仅会因 JSON 序列化导致精度丢失(big.Int 被截断为 int64),更严重的是破坏了 JWT 的信任模型:若公钥本身可被任意篡改并嵌入 token,则签名完全失去防伪意义。
✅ 正确做法是:*将公钥序列化为标准 PEM 格式字符串(Base64 编码的 ASN.1 DER 结构),存入 claims;解析时再从该字符串反序列化为 `rsa.PublicKey**。PEM 是文本安全、标准兼容且无精度损失的表示方式,且jwt-go提供了开箱即用的jwt.ParseRSAPublicKeyFromPEM()` 辅助函数。
以下是完整可运行示例(基于 github.com/dgrijalva/jwt-go v3.x):
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"log"
jwt "github.com/dgrijalva/jwt-go"
)
func main() {
// 1. 生成 RSA 密钥对(生产环境请使用 ≥2048 位)
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Fatal("生成私钥失败:", err)
}
// 2. 构建 token 并嵌入 PEM 格式公钥
token := jwt.New(jwt.SigningMethodRS256)
token.Claims = jwt.MapClaims{
"username": "victorsamuelmd",
"exp": time.Now().Add(time.Hour).Unix(),
// ✅ 关键:将公钥 Marshal 为 PKIX DER,再编码为 PEM 字符串
"public_key_pem": pemEncodePublicKey(&privateKey.PublicKey),
}
tokenString, err := token.SignedString(privateKey)
if err != nil {
log.Fatal("签名 token 失败:", err)
}
fmt.Println("已生成 token:", tokenString)
// 3. 解析 token 并动态提取公钥验签
parsedToken, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
// 验证签名算法是否符合预期
if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("意外的签名方法: %v", t.Header["alg"])
}
// 从 claims 中读取 PEM 字符串并解析为 *rsa.PublicKey
claims, ok := t.Claims.(jwt.MapClaims)
if !ok {
return nil, fmt.Errorf("claims 类型断言失败")
}
pemStr, ok := claims["public_key_pem"].(string)
if !ok {
return nil, fmt.Errorf("claims 中未找到 public_key_pem 字段或类型错误")
}
return jwt.ParseRSAPublicKeyFromPEM([]byte(pemStr))
})
if err != nil {
log.Fatal("解析/验签失败:", err)
}
if !parsedToken.Valid {
log.Fatal("token 验证失败:签名无效或已过期")
}
fmt.Println("✅ token 验证成功!Payload:", parsedToken.Claims)
}
// pemEncodePublicKey 将 *rsa.PublicKey 转为 PEM 格式字符串
f
unc pemEncodePublicKey(pub *rsa.PublicKey) string {
bytes, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
panic("公钥序列化失败: " + err.Error())
}
pemBlock := &pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: bytes,
}
return string(pem.EncodeToMemory(pemBlock))
}⚠️ 重要注意事项:
- 安全性警示:此方案仅适用于可信上下文(如内部微服务通信、受控 API 网关)。若 token 可能被恶意第三方截获并重放,嵌入公钥会削弱“唯一可信签发者”模型——攻击者可伪造含自己公钥的 token。此时应坚持传统方式:服务端维护权威公钥池,通过 kid(Key ID)字段索引。
- 性能考量:每次解析都需 PEM 解析和 ASN.1 解码,比直接使用内存公钥稍慢。高并发场景建议缓存已解析的公钥(以 PEM 字符串为 key)。
- 密钥长度:示例使用 2048 位;生产环境推荐 3072 或 4096 位以满足长期安全要求。
- 库迁移提示:dgrijalva/jwt-go 已归档,新项目推荐迁移到 golang-jwt/jwt/v5,其 API 更清晰(如 jwt.WithValidMethods, jwt.WithValidator),且原生支持 func(token *Token) (any, error) 形式的 KeyFunc。
总结:通过 PEM 字符串嵌入公钥是一种实用的动态验签技术,它平衡了灵活性与标准兼容性。关键在于理解其适用边界——它不是替代传统公钥管理的银弹,而是特定架构下的有力补充。
# js
# git
# json
# go
# github
# golang
# 编码
# ai
# unix
# crypto
# 架构
# Error
# Token
# 字符串
# int
# 并发
# 公钥
# 适用于
# 服务端
# 的是
# 序列化
# 是一种
# 并在
# 请使用
# 第三方
# 时应
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
*服务器网站为何频现安全漏洞?
如何在云虚拟主机上快速搭建个人网站?
Laravel如何优雅地处理服务层_在Laravel中使用Service层和Repository层
JS去除重复并统计数量的实现方法
Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】
Laravel如何实现多表关联模型定义_Laravel多对多关系及中间表数据存取【方法】
Laravel如何实现本地化和多语言支持?(i18n教程)
如何破解联通资金短缺导致的基站建设难题?
Laravel如何配置和使用队列处理异步任务_Laravel队列驱动与任务分发实例
Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程
Laravel项目结构怎么组织_大型Laravel应用的最佳目录结构实践
Laravel中DTO是什么概念_在Laravel项目中使用数据传输对象(DTO)
历史网站制作软件,华为如何找回被删除的网站?
javascript读取文本节点方法小结
北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?
如何快速搭建高效WAP手机网站吸引移动用户?
php打包exe后无法访问网络共享_共享权限设置方法【教程】
Android okhttputils现在进度显示实例代码
香港服务器WordPress建站指南:SEO优化与高效部署策略
Laravel如何实现邮件验证激活账户_Laravel内置MustVerifyEmail接口配置【步骤】
ChatGPT怎么生成Excel公式_ChatGPT公式生成方法【指南】
html5的keygen标签为什么废弃_替代方案说明【解答】
Laravel API资源类怎么用_Laravel API Resource数据转换
Laravel PHP版本要求一览_Laravel各版本环境要求对照
JavaScript中如何操作剪贴板_ClipboardAPI怎么用
bootstrap日历插件datetimepicker使用方法
胶州企业网站制作公司,青岛石头网络科技有限公司怎么样?
如何在景安服务器上快速搭建个人网站?
详解MySQL数据库的安装与密码配置
原生JS获取元素集合的子元素宽度实例
如何在阿里云部署织梦网站?
Laravel辅助函数有哪些_Laravel Helpers常用助手函数大全
HTML 中如何正确使用模板变量为元素的 name 属性赋值
Laravel如何处理和验证JSON类型的数据库字段
阿里云网站搭建费用解析:服务器价格与建站成本优化指南
香港服务器租用费用高吗?如何避免常见误区?
Windows11怎样设置电源计划_Windows11电源计划调整攻略【指南】
Laravel如何实现图片防盗链功能_Laravel中间件验证Referer来源请求【方案】
Laravel怎么实现验证码功能_Laravel集成验证码库防止机器人注册
Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录
Win10如何卸载预装Edge扩展_Win10卸载Edge扩展教程【方法】
Laravel怎么在Controller之外的地方验证数据
如何确认建站备案号应放置的具体位置?
制作电商网页,电商供应链怎么做?
如何基于云服务器快速搭建个人网站?
iOS发送验证码倒计时应用
ai格式如何转html_将AI设计稿转换为HTML页面流程【页面】
Python企业级消息系统教程_KafkaRabbitMQ高并发应用
如何在景安云服务器上绑定域名并配置虚拟主机?
使用Dockerfile构建java web环境


