JWT(JSON Web Token)是开发中常用的身份认证令牌,调试时经常需要快速查看其中的 header 和 payload 内容。每次都要复制到 jwt.io 太麻烦,自己写一个命令行工具又快又趁手。
本文将展示如何用 OpenCode 这款终端 AI 编程助手,在 4 分钟内用 Go 写出一个实用的 JWT 解析工具,支持参数输入、管道输入、过期时间展示等功能,最终代码约 70 行。
打开终端,进入项目目录,直接对 OpenCode 说出需求:
> 我对 OpenCode 说:
>
> 帮我用 Go 写一个命令行 JWT 解析工具,接收一个 JWT token 作为参数,用 . 分割成三部分,对 header 和 payload 做 base64url 解码,然后格式化输出 JSON。
OpenCode 会分析需求,创建 main.go,自动处理边界条件和错误信息:
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"os"
"strings"
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "Usage: jwtdec <token>")
os.Exit(1)
}
parts := strings.Split(os.Args[1], ".")
if len(parts) != 3 {
fmt.Fprintln(os.Stderr, "Error: invalid JWT format")
os.Exit(1)
}
printPart("Header", parts[0])
printPart("Payload", parts[1])
}
func printPart(label, encoded string) {
decoded, err := base64.RawURLEncoding.DecodeString(encoded)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s decoding failed\n", label)
os.Exit(1)
}
var data map[string]interface{}
json.Unmarshal(decoded, &data)
out, _ := json.MarshalIndent(data, "", " ")
fmt.Printf("=== %s ===\n%s\n", label, out)
}
关键点:OpenCode 自动选择了 base64.RawURLEncoding(JWT 使用不带 = 填充的 URL 安全编码),无需我们手动指定。
命令行工具要能跟管道配合才顺手:
> 我对 OpenCode 说:
>
> 如果命令行参数为空,就从 stdin 读取 token。
import "io"
// main 中参数解析改为:
token := ""
if len(os.Args) > 1 {
token = os.Args[1]
} else {
data, _ := io.ReadAll(os.Stdin)
token = strings.TrimSpace(string(data))
}
这样既能 jwtdec eyJhbG... 直接传参,也能 echo "eyJhbG..." | jwtdec 管道输入。
JWT payload 中常包含 exp 字段(Unix 时间戳),直接显示可读的过期时间更实用:
> 我对 OpenCode 说:
>
> 解析 payload 中的 exp 字段,格式化显示过期时间和剩余有效期。
OpenCode 在 printPart 函数末尾追加了过期判断逻辑,同时自动引入了 time 包:
if exp, ok := data["exp"]; ok {
if expFloat, ok := exp.(float64); ok {
expTime := time.Unix(int64(expFloat), 0)
remaining := time.Until(expTime)
if remaining > 0 {
fmt.Printf(" Expires in: %v\n", remaining.Round(time.Second))
} else {
fmt.Printf(" Expired %v ago\n", (-remaining).Round(time.Second))
}
}
}
JSON 中的数字经 json.Unmarshal 到 interface{} 会变成 float64,OpenCode 正确处理了这个类型转换细节。
有时我们只需要原始 JSON 内容,不需要缩进:
> 我对 OpenCode 说:
>
> 加一个 -c 的 flag 参数,传了 -c 就紧凑输出(不带缩进),否则默认带缩进。
OpenCode 使用 flag 标准库添加参数解析,并通过条件判断控制 json.MarshalIndent 的缩进参数:
var compact bool flag.BoolVar(&compact, "c", false, "Compact output") flag.Parse()
整个交互过程不到 4 分钟,每一个功能都是直接用自然语言描述需求,OpenCode 负责翻译成正确的 Go 代码。
package main
import (
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"io"
"os"
"strings"
"time"
)
func main() {
var compact bool
flag.BoolVar(&compact, "c", false, "Compact output")
flag.Parse()
token := ""
if args := flag.Args(); len(args) > 0 {
token = args[0]
} else {
data, _ := io.ReadAll(os.Stdin)
token = strings.TrimSpace(string(data))
}
if token == "" {
fmt.Fprintln(os.Stderr, "Usage: jwtdec [-c] <token>")
fmt.Fprintln(os.Stderr, " echo <token> | jwtdec [-c]")
os.Exit(1)
}
parts := strings.Split(token, ".")
if len(parts) != 3 {
fmt.Fprintln(os.Stderr, "Error: invalid JWT format")
os.Exit(1)
}
printPart("Header", parts[0], compact)
printPart("Payload", parts[1], compact)
fmt.Printf("\nSignature: %s\n", parts[2])
}
func printPart(label, encoded string, compact bool) {
decoded, err := base64.RawURLEncoding.DecodeString(encoded)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: invalid %s encoding\n", label)
os.Exit(1)
}
var data map[string]interface{}
if err := json.Unmarshal(decoded, &data); err != nil {
fmt.Fprintf(os.Stderr, "Error: invalid %s JSON\n", label)
os.Exit(1)
}
indent := " "
if compact {
indent = ""
}
out, _ := json.MarshalIndent(data, "", indent)
fmt.Printf("=== %s ===\n%s\n", label, out)
if exp, ok := data["exp"]; ok {
if expFloat, ok := exp.(float64); ok {
expTime := time.Unix(int64(expFloat), 0)
remaining := time.Until(expTime)
if remaining > 0 {
fmt.Printf(" Expires in: %v\n", remaining.Round(time.Second))
} else {
fmt.Printf(" Expired %v ago\n", (-remaining).Round(time.Second))
}
}
}
}
编译运行:
go build -o jwtdec . ./jwtdec eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoxODkzNDU2MDAwfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
输出效果:
=== Header ===
{
"alg": "HS256",
"typ": "JWT"
}
=== Payload ===
{
"exp": 1893456000,
"name": "John Doe",
"sub": "1234567890"
}
Expires in: 95040h0m0s
Signature: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
整个开发过程不到 4 分钟,四个功能全部由自然语言驱动,OpenCode 完成了代码编写、类型处理、错误处理等全部细节工作。
与传统手动编码相比,用 OpenCode 开发这类小工具的效率提升非常明显:不用查 API 文档、不用处理类型转换细节、不用纠结错误处理写法——你只需要想清楚"要做什么",AI 帮你解决"怎么做"。这正是终端 AI 编程助手适合的场景:快速验证想法,把小需求立刻变成可运行的工具。
如果你也经常需要在终端里处理各种数据格式,不妨试试用 OpenCode 打造自己的命令行工具箱。