因为JWT的alg字段可被篡改导致none攻击,而PASETO通过强制版本与目的分离(v2.local/v2.public)、禁用不安全算法、锁定签名加密逻辑来杜绝误用。

为什么不用JWT而选PASETO?
因为JWT的alg字段可被篡改,攻击者能伪造none签名绕过验证;而PASETO强制版本与目的分离(v2.local / v2.public),禁用不安全算法,签名和加密逻辑由库内部锁定——你写不出不安全的token。
github.com/o1egl/paseto 初始化时必须设对版本和密钥类型
初始化Paseto实例时,不能只传密钥,必须显式指定用途和版本。错用会导致invalid token或unsupported version错误,且这类错误不会提示具体原因。
-
v2.local用于服务端加密场景(如敏感会话数据),需传入32字节对称密钥:key := make([]byte, 32),用rand.Read(key)生成 -
v2.public用于无状态签名(如API访问凭证),需用ed25519.GenerateKey生成密钥对,私钥签名、公钥验签 - 别把
v2.local密钥当v2.public私钥用——二者长度、语义、加解密路径完全不同
Gin中间件里解析v2.local token要手动解密+反序列化
不同于JWT直接解析JSON payload,v2.local token是加密后的二进制块,必须先解密再JSON Unmarshal。漏掉任一环节都会panic或返回空结构体。
- 调用
paseto.Decrypt时传入密钥和token字符串,返回[]byte原始payload - 必须用
json.Unmarshal把解密后字节转成你的Payload结构体,不能跳过这步直接断言 - 注意:
Decrypt不校验过期时间,你需要自己从解密后的exp字段检查time.Now().After(exp)
公钥模式v2.public验签失败常见于时钟偏移和公钥格式
验签失败时,Verify函数只返回error,不说明是签名错、过期还是公钥不匹配。最容易忽略的是两点:
立即学习“go语言免费学习笔记(深入)”;
- 公钥必须是
ed25519.PublicKey类型,不是*rsa.PublicKey或PEM字节切片;需用ed25519.PublicKey(pubKeyBytes)显式转换 - 客户端和服务端系统时间差超过
10s(默认容忍窗口)就会报token expired,即使token本身没过期;建议NTP同步或调大WithClockSkew选项
Decrypt和Verify调用都得严格对照v2规范的输入约束。


















