lestrrat-go/jwx
GitHub: lestrrat-go/jwx
Go 语言下完整实现 JOSE/JWx 全套规范(JWT、JWS、JWE、JWK、JWA)的密码学与身份令牌处理库。
Stars: 2405 | Forks: 193
# github.com/lestrrat-go/jwx/v4 [](https://github.com/lestrrat-go/jwx/actions/workflows/ci.yml) [](https://pkg.go.dev/github.com/lestrrat-go/jwx/v4) [](https://deepwiki.com/lestrrat-go/jwx)
Go module 实现了各种 JWx(JWA/JWE/JWK/JWS/JWT,也称为 JOSE)技术。
如果您正在您的产品或公司中使用此 module,请在 [Wiki](https://github.com/lestrrat-go/jwx/wiki/Users) 中添加您的产品和/或公司名称!这真的有助于保持我们的动力。
# 环境要求
* Go 1.26 或更高版本
* `GOEXPERIMENT=jsonv2`
# 安装
```
go get github.com/lestrrat-go/jwx/v4
```
## Claude Code Skill
如果您使用 [Claude Code](https://claude.com/claude-code),请安装内置的 `jwx-dev-v4` skill,这样助手就能指导您使用该库——包括选择算法、解析/签名 JWT、使用 JWS/JWE/JWK,并避免常见的陷阱:
```
/plugin marketplace add lestrrat-go/jwx
/plugin install jwx-dev-v4
```
此 skill 仅适用于 **v4**。它面向*使用* jwx 的开发者,而不是用于开发库本身。
# 从 v3 迁移
如果您正在从 `github.com/lestrrat-go/jwx/v3` 迁移,请参阅 [`MIGRATION.md`](MIGRATION.md) 获取包含修改前后代码示例的逐步指南。有关破坏性变更和新功能的完整列表,请参阅 [`Changes-v4.md`](Changes-v4.md)。
# 功能
| 功能 | 描述 |
|---------|-------------|
| **全面的 JWA/JWE/JWK/JWS/JWT 覆盖** | 不仅仅是 JWT + 最小工具集。支持带有多个签名的 JWS 消息(compact 和 JSON 序列化)、带有 detached payload 的 JWS、带有 unencoded payload 的 JWS (RFC 7797),以及带有多个接收者的 JWE 消息(compact 和 JSON 序列化)。大多数操作可以使用 JWK 或原始密钥(例如 `*rsa.PrivateKey`、`*ecdsa.PrivateKey`)。 |
| **具有主见且统一的 API** | 一切都是对称的,并遵循标准约定:`jws.Parse`/`Verify`/`Sign`,`jwe.Parse`/`Encrypt`/`Decrypt`。参数被组织为明确的必填参数和可选的 `WithXXXX()` 风格选项。 |
| **后量子密码学** | 支持 ML-KEM、ML-DSA 和 HPKE。 |
| **扩展 module 架构** | 通过扩展 module 提供可选功能。请参阅[扩展 Module](docs/10-extensions.md)。 |
| **JWK 缓存** | [`jwkfetch`](https://github.com/jwx-go/jwkfetch) 扩展,用于始终保持 JWKS 最新。 |
| **Bazel 支持** | 支持 [Bazel](https://bazel.build)。 |
# 概要
```
package examples_test
import (
"bytes"
"fmt"
"net/http"
"time"
"github.com/lestrrat-go/jwx/v4/jwa"
"github.com/lestrrat-go/jwx/v4/jwe"
"github.com/lestrrat-go/jwx/v4/jwk"
"github.com/lestrrat-go/jwx/v4/jws"
"github.com/lestrrat-go/jwx/v4/jwt"
)
func Example() {
// Parse, serialize, slice and dice JWKs!
privkey, err := jwk.ParseKey(jsonRSAPrivateKey)
if err != nil {
fmt.Printf("failed to parse JWK: %s\n", err)
return
}
pubkey, err := jwk.PublicKeyOf(privkey)
if err != nil {
fmt.Printf("failed to get public key: %s\n", err)
return
}
// Work with JWTs!
{
// Build a JWT!
tok, err := jwt.NewBuilder().
Issuer(`github.com/lestrrat-go/jwx`).
IssuedAt(time.Now()).
Build()
if err != nil {
fmt.Printf("failed to build token: %s\n", err)
return
}
// Sign a JWT!
signed, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256(), privkey))
if err != nil {
fmt.Printf("failed to sign token: %s\n", err)
return
}
// Verify a JWT!
{
verifiedToken, err := jwt.Parse(signed, jwt.WithKey(jwa.RS256(), pubkey))
if err != nil {
fmt.Printf("failed to verify JWS: %s\n", err)
return
}
_ = verifiedToken
}
// Work with *http.Request!
{
req, _ := http.NewRequest(http.MethodGet, `https://github.com/lestrrat-go/jwx`, nil)
req.Header.Set(`Authorization`, fmt.Sprintf(`Bearer %s`, signed))
verifiedToken, err := jwt.ParseRequest(req, jwt.WithKey(jwa.RS256(), pubkey))
if err != nil {
fmt.Printf("failed to verify token from HTTP request: %s\n", err)
return
}
_ = verifiedToken
}
}
// Encrypt and Decrypt arbitrary payload with JWE!
{
encrypted, err := jwe.Encrypt(payloadLoremIpsum, jwe.WithKey(jwa.RSA_OAEP_256(), jwkRSAPublicKey))
if err != nil {
fmt.Printf("failed to encrypt payload: %s\n", err)
return
}
decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP_256(), jwkRSAPrivateKey))
if err != nil {
fmt.Printf("failed to decrypt payload: %s\n", err)
return
}
if !bytes.Equal(decrypted, payloadLoremIpsum) {
fmt.Printf("verified payload did not match\n")
return
}
}
// Sign and Verify arbitrary payload with JWS!
{
signed, err := jws.Sign(payloadLoremIpsum, jws.WithKey(jwa.RS256(), jwkRSAPrivateKey))
if err != nil {
fmt.Printf("failed to sign payload: %s\n", err)
return
}
verified, err := jws.Verify(signed, jws.WithKey(jwa.RS256(), jwkRSAPublicKey))
if err != nil {
fmt.Printf("failed to verify payload: %s\n", err)
return
}
if !bytes.Equal(verified, payloadLoremIpsum) {
fmt.Printf("verified payload did not match\n")
return
}
}
// OUTPUT:
}
```
源码: [examples/jwx_readme_example_test.go](https://github.com/jwx-go/examples/blob/v4/jwx_readme_example_test.go)
# 文档
* [API 参考](https://pkg.go.dev/github.com/lestrrat-go/jwx/v4)
* [操作指南文档](./docs)
* [可运行示例](https://github.com/jwx-go/examples)
* [扩展 Module](docs/10-extensions.md)
# 包和标准
此 module 实现了以下规范:
| 包 | 规范 |
|---------|---------------|
| [jwa](./jwa) | [RFC 7518](https://tools.ietf.org/html/rfc7518) (JSON Web Algorithms) |
| [jwk](./jwk) | [RFC 7517](https://tools.ietf.org/html/rfc7517) (JSON Web Key), [RFC 7638](https://tools.ietf.org/html/rfc7638) (JWK Thumbprint), [RFC 8037](https://tools.ietf.org/html/rfc8037) (CFRG Curves) |
| [jws](./jws) | [RFC 7515](https://tools.ietf.org/html/rfc7515) (JSON Web Signature), [RFC 7797](https://tools.ietf.org/html/rfc7797) (Unencoded Payload) |
| [jwe](./jwe) | [RFC 7516](https://tools.ietf.org/html/rfc7516) (JSON Web Encryption), [draft-ietf-jose-hpke-encrypt](https://datatracker.ietf.org/doc/draft-ietf-jose-hpke-encrypt/) (HPKE) |
| [jwt](./jwt) | [RFC 7519](https://tools.ietf.org/html/rfc7519) (JSON Web Token) |
此外,通过主 module 或[扩展 module](docs/10-extensions.md) 还提供了额外支持:
## 历史
我的目标是编写一个大量使用 JWK 和 JWT 的服务器。乍一看,
已有的库似乎足够了,但很快我意识到
1. 要完全实现这些协议,我需要完整的 JWT、JWK、JWS、JWE(以及必然需要的 JWA)。
2. 现存的大多数库只处理各种 JWx 规范中的一部分,这些只是实现它们特定需求所必需的。
因为我正在编写服务器端(以及用于测试的客户端),我需要*整个* JOSE 工具集来正确实现我的服务器,**并且**它们需要足够*灵活*以满足我正在编写的整个规范。
所以就有了 `github.com/lestrrat-go/jwx/v4`。这个库是可扩展的、可定制的,并且组织得很好,方便您轻松地对它进行切割和取舍。
## 为什么我要使用这个库?
还有其他几个主要的 Go module 可以处理 JWT 和相关数据格式,
那么为什么您应该使用这个库呢?
从纯粹的功能角度来看,唯一的主要区别是:
虽然大多数其他项目只处理它们认为处理 JWT 所必需的内容,
但这个 module 处理 JWS、JWE、JWK 和 JWT 的**_整个_**范畴。
也就是说,如果您不仅需要解析 JWT,还需要控制 JWK,或者
如果您需要处理不是 JWT 的 payload,您应该考虑
使用这个 module。您还应该注意,JWT 是构建在这些
其他技术*之上*的。如果没有实现 JWS/JWE/JWK 的全部功能,您根本无法拥有一个完整的 JWT 包,而这个库做到了这一点。
其次,从实现的角度来看,这个 module 与其他库有明显不同,
因为它非常努力地只暴露 API,而不暴露
内部数据。例如,不能通过 struct field 查找来访问单个 JWT claim。您需要使用其中一种 getter 方法。
这是因为这个库的立场是,最终用户在遇到宽松的 API 时,完全有能力甚至很乐意搬起石头砸自己的脚。通过确保用户无法访问未封闭的 struct,我们可以保护用户不做诸如创建*不完整*的 struct,或者在没有任何保护的情况下并发访问 struct 之类的蠢事。这种结构还允许我们在 struct 中注入额外的智能,例如,当您想解析/写入自定义字段时做正确的事情(这个 module 不需要用户指定备用 struct 来解析带有自定义字段的对象)。
归根结底,我认为这取决于您的使用模式和优先级。
我想到的一些一般性指导原则是:
* 如果您想用一个库来处理所有与 JWx 相关的事情,比如使用 JWE、JWK、JWS,处理[自动刷新 JWK](https://github.com/jwx-go/jwkfetch),请使用此 module。
* 如果您想透明地兑现所有可能的自定义字段,请使用此 module。
* 如果您想要一个标准化且干净的 API,请使用此 module。
否则,请随意选择其他库。
# 贡献
## Issues
## Pull Requests
请确保包含测试,以验证您所做的更改。
如果您正在编辑自动生成的文件(带有 `_gen.go` 后缀的文件),请确保您执行了以下操作:
1. 修改生成器,而不是生成的文件(例如 `internal/jwxcodegen/cmd/jwxcodegen/`)
2. 运行 `make generate`(或 `go generate`)来生成新代码
3. 同时提交生成器_和_生成的文件
## 讨论 / 用法
请先尝试[讨论区](https://github.com/lestrrat-go/jwx/discussions)。
# 交换条件
如果您使用此软件在盈利性组织中构建产品,我们请求您_考虑_
按以下方式回馈 FOSS:
这_不是_许可条款:您仍然可以根据该软件附带的许可证自由使用它。此条款仅是对人们的一种呼吁,希望大家能够认可 FOSS 开发者的工作,您每天都在依赖他们的成果。
标签:EVTX分析, 日志审计