Go使用JWT
Go使用JWT
标签(空格分隔):go,auth,jwt
访问官网 【https://jwt.io/】
jwt生成的token,是三部分组成 由.分割
`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c`
- 第一部分:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Header(头部)
{ "alg": "HS256", // 加密算法 "typ": "JWT" // 类型 JWT }
- 第二部分:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Payload(负载)
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, // 签发时间 "exp": 1516239022, // 过期时间 "issuer":"auth", // 签发人 }
- 第三部分:SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Signature(签名)
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), 【your-256-bit-secret】 ) secret base64 encoded
我使用的第三方包是:https://github.com/dgrijalva/jwt-go
Encode 加密方式为:RS512
package token
import (
"car/shared/id"
"crypto/rsa"
"github.com/dgrijalva/jwt-go"
"time"
)
type JWTTokenGen struct {
privateKey *rsa.PrivateKey
issuer string
nowFunc func() time.Time
}
func NewJWTTokenGen(issuer string, privateKey *rsa.PrivateKey) *JWTTokenGen {
return &JWTTokenGen{
privateKey: privateKey,
issuer: issuer,
nowFunc: time.Now,
}
}
func (j *JWTTokenGen) GeneratorToken(accountId id.AccountId, expired time.Duration) (string, error) {
now := j.nowFunc().Unix()
exp := now + int64(expired.Seconds())
token := jwt.NewWithClaims(jwt.SigningMethodRS512, jwt.StandardClaims{
ExpiresAt: exp,
IssuedAt: now,
Issuer: j.issuer,
Subject: accountId.String(),
})
return token.SignedString(j.privateKey)
}
# test测试
package token
import (
"car/shared/id"
"github.com/dgrijalva/jwt-go"
"testing"
"time"
)
const privateKey = `-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC3+K2kVZ8Rhbqd
Zm2lvbT1nxxxJKamY+4uyyeOeDkMZMuLxyg4fFAbGJ0yNyPFa7h+6foOUEQbwLvy
wZVOsaF7yQgMH7ROfS5v7C1i70FbjfkPblCQLnbwUB6p6yM+Dag68o1FpK91pVc7
WJr+ex8pWwt6Xva9t37tWabyXMorB55dTtYdKN3KPAy9kmA5rbJAhHfb64OABJwA
OwWhQk2ccq62O7Na9I4XZcRgMmdSkbaKHd5HWJKJ3KXt8jWQDNHDm71F9yMmSfqq
K7gOhcLWmsRiUQDYOHltGn8qhACTha69Rx1TRYWm/hyKzwpWlTBhM1BSL0cbG7ab
TDjuaHD1AgMBAAECggEAVDeJvc3YSJwwRneqQBIs8bfJvF2r0EW0tkUXsZeFfg/y
QeWEmv8FaLIG3ALz5B8g0TBo4P6LfyzmnQVTRTf5lYH8phgpYTja9i9mw4CFKdU/
K3ozdR1gIzh6IQzcED8OQRXh5/ywualb2nYAVfik1jiwc29fgo+qkhrVWuNxROqs
LsXlyMAodTZIAPnrFnT9XYtwhS4+JIraIe/7d3dy/VL8NN54tdfTj+nhA8r2K2/y
6tBNerndLCyulpTQdYYIpV4WWCwK0x9CRnk5uYI9cIihrm7uJ4cXMEHF9LozdI+Y
cijkHZ1j/GEBiVWDAf6Bjh7CesHWuduSl2uXPik9zQKBgQDdo6+g/cBfAOBgBHFx
mykOip45R8iFtVkQ6Kls+e/0o8h8oebYtbFB3WYDI+Ds6Oc6SoCejg2Gxhszcu1l
gUwedo1E5Yyuu0wXCB9+A/W/TEaYcb8SbBle/rRxf+WdVAFQajhPL+jC6drERUHG
9yjK75OWOaGfQl075NOlDUbu8wKBgQDUfgsg9kvkBP1BMi3b7+oKCveOtQz5LVin
89+/PqfwTGH+MyFGgCgXR/1Oc6wgBM3nK2xVwYHub65GhBpiSR1Vbx08H1+m5eat
CET4RVJJRAzMpF+MFWXlVe0aqHVgVPOfTkJm+CEthfsCgvsW0T/6IHsBDWXe1hIX
Qplj3baqdwKBgASoKMuIKV+VpCFAVqXdbbppfTOU54ivq8RoLw8kHT+VpWPA/xm0
j5njrf4I5fP0XM9ju1oZf/1B8lZdZtGIKvJE6Wj2LcOKGeot+INsT+CKGL2hPjOD
4/CUB3SPDGMG3dQztyUqy4g046zWC7DDhq7k48P3SAcRnOXiQ/Qm2EOzAoGANBaF
odneNpHmhtHHHg/MfhO8JXZKLNap0dndMiMoxaXjq41bDc4ihbp25IFEY0DqPE2P
q+HYeNtVjCGb2hX0I9ot/2vtZZsblbTeO0G67wzIWm28ojkoljQfABblVuzcH2Pu
sREICQG7yxzScgBvP2Aw5swi+aHeI2eG9E0IFvcCgYApo4CJVYUsYkujJUdFmN/s
28SVVnX0G5+qBMiPvmybsc9+Fl9a3BgsyC+uaX3RxMFZLCRTeKZNxJ3+TbulJ2Hf
zYelZmjw0cU2in3zSf+N/1w0B1yd+ISHB8KARQLGLdMfrrN22J4gS9seIi5p1Vxn
jsURET4gnIFKE01t7IlI+w==
-----END PRIVATE KEY-----`
func TestJWTTokenGen_GeneratorToken(t *testing.T) {
pem, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey))
if err != nil {
t.Fatalf("parse rsa private key error: %v\n", err)
}
accountId := "o1dDm4g17xaNm6W3HBh9bzVMtW5Y"
class := NewJWTTokenGen("car-auth", pem)
class.nowFunc = func() time.Time {
return time.Unix(1684209185, 0)
}
token, err := class.GeneratorToken(id.AccountId(accountId), 2*time.Hour)
if err != nil {
t.Errorf("genrator token error %v\n", err)
}
want := "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODQyMTYzODUsImlhdCI6MTY4NDIwOTE4NSwiaXNzIjoiY2FyLWF1dGgiLCJzdWIiOiJvMWREbTRnMTd4YU5tNlczSEJoOWJ6Vk10VzVZIn0.sHr9TgB1U8n0AQhIAoLCsBPyFaOcFda2DRMytbR6eXJS3D8y1iMLaA2FM9MvMj4iF4-EGcv4jHiGwGnDjbc-2kQVYheszm9cLFtLZ8FLRSBp_cLw-fCwy0_6_kcsLI3bLZdANxuaOHva9kNvL2jks-iLdQNB2HlKoaZafH24r7P1fUOHmzE0RbrKTYpPaAXxI38SBbtHk6lamJNQSfoKCrqYA2LcgUDUd0mQCCNBQHPrIntBBaV2oOwd61tOIkGrgDExFmqQ3c_kGTSenrZrUCGY28hx8FuG6v_w9jDYi6c-U-jAkiRyNVA3IkbaA_svQdvmLxGuMqxcCsrwkhyQ9g"
if token != want {
t.Errorf("want error \n want %q \n token %q \n", want, token)
}
}
Decode 解密
package token
import (
"crypto/rsa"
"fmt"
"github.com/dgrijalva/jwt-go"
)
type JWTTokenVerifier struct {
PublicKey *rsa.PublicKey
}
func NewJWTTokenVerifier(key *rsa.PublicKey) *JWTTokenVerifier {
return &JWTTokenVerifier{
PublicKey: key,
}
}
func (j *JWTTokenVerifier) Verify(token string) (string, error) {
t, err := jwt.ParseWithClaims(token, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
return j.PublicKey, nil
})
if err != nil {
return "", err
}
if !t.Valid {
return "", fmt.Errorf("token is not valid")
}
claims, ok := t.Claims.(*jwt.StandardClaims)
if !ok {
return "", fmt.Errorf("token claims is not StandarClaims")
}
err = claims.Valid()
if err != nil {
return "", err
}
return claims.Subject, nil
}
# test测试
package token
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"testing"
)
const PublicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAztkC5tzKUvwoy9gYVfgk
LqrybhF8eJ0hV/+vnmSxyuCC53Hyyk3IOlKX3cl62icqRr5oCM1KV9BvjzsZS8Pe
kcmCO2J8Z+EaaAhSq5wteoxEzmuMjyWWn2BhhF8ZjYeSckQy3BCtaCW7aNKMbklL
Q97FSvKIhYllwokqgPr8M9eHwAtiIHXRc5f+5VGqgSSVZFYNah7Rrdp1pbdJWNhV
l4nDcGQBKoB3VrISZgUJQyHIaGiL+Q/ehDUAiVXjQr/rveU8IgijfEK075vmMZXN
935rx4tR1AQDGgi3QO3muIw/1pziIo05VeSt0k3Vy+Jlb3K3ggk3dg97I/4mEmFE
vQIDAQAB
-----END PUBLIC KEY-----`
func TestJWTTokenVerifier_Verify(t *testing.T) {
pem, err := jwt.ParseRSAPublicKeyFromPEM([]byte(PublicKey))
if err != nil {
t.Fatalf("parse public.key file error: %v\n", err)
}
class := NewJWTTokenVerifier(pem)
verify, err := class.Verify("eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODQyMTY4OTMsImlhdCI6MTY4NDIxMTI4NiwiaXNzIjoiY2FyLWF1dGgiLCJzdWIiOiI2NDYxZmQ2YWYwYzQ1ZTllMDBhZDJhMDUifQ.WWsALGlLVzfKKy96cIHh0KI3CdP1MJy70SxBVWi89k10qJD5ib8haK-bz9OJDCd5ciBz_6YetLhmr9XSN1QaqdwmAyqr8NWhFhl_S8eb2hIkxO0rk010xQDcO4YZvhSHlJBm8S2HgTb_Lc1OISgZ18r7IupQe7ZrXV3exlNGygdh1mMmWUYP0LAKKpQpEHMQIfb7jbPpfS6W3bSXfrVysz2bJz2b-adpfea9oLYEzLoaw1TUmTWMeTXjjqycnqwN4ENt1s9LdekyOgQ0P2WvAKaA0ER38CmI2WtEEAxRYnIZASO0x591B24Ket4Tgh_va4rbJx1xeogcsZIqX6Z3sg")
if err != nil {
t.Fatalf("token verify error: %v\n", err)
}
fmt.Println(verify)
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
2019-05-30 TP5异常处理