golang实现JWT生成验证与签名的全功能插件库jwt-go的使用
golang实现JWT生成验证与签名的全功能插件库jwt-go的使用
什么是JWT
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。它由三部分组成,用点(.)分隔:
- 头部(Header) - 包含令牌类型和签名算法
- 载荷(Payload) - 包含声明(claims),即要传输的数据
- 签名(Signature) - 用于验证消息未被篡改
jwt-go库简介
jwt-go是Go语言实现的JWT库,支持JWT的解析、验证、生成和签名功能。当前支持的签名算法包括:
- HMAC SHA
- RSA
- RSA-PSS
- ECDSA
安装
go get -u github.com/golang-jwt/jwt/v5
使用示例
1. 生成JWT令牌
package main
import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
// 自定义Claims结构体,可以添加自定义字段
type CustomClaims struct {
UserID int64 `json:"user_id"`
jwt.RegisteredClaims
}
func main() {
// 定义HMAC密钥
hmacSecret := []byte("your-secret-key")
// 创建Claims
claims := CustomClaims{
UserID: 12345,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "test",
Subject: "somebody",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), // 过期时间24小时
IssuedAt: jwt.NewNumericDate(time.Now()), // 签发时间
},
}
// 使用HS256算法创建token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 签名token
tokenString, err := token.SignedString(hmacSecret)
if err != nil {
fmt.Println("Error generating token:", err)
return
}
fmt.Println("Generated token:", tokenString)
}
2. 验证JWT令牌
package main
import (
"fmt"
"github.com/golang-jwt/jwt/v5"
)
type CustomClaims struct {
UserID int64 `json:"user_id"`
jwt.RegisteredClaims
}
func main() {
// 假设这是从客户端接收到的token
tokenString := "your.jwt.token.string"
// 定义HMAC密钥(与生成时相同)
hmacSecret := []byte("your-secret-key")
// 解析token
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
// 验证签名算法是否正确
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return hmacSecret, nil
})
if err != nil {
fmt.Println("Error parsing token:", err)
return
}
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
fmt.Printf("UserID: %v, ExpiresAt: %v\n", claims.UserID, claims.ExpiresAt)
} else {
fmt.Println("Invalid token")
}
}
3. 使用RSA签名
package main
import (
"crypto/rand"
"crypto/rsa"
"fmt"
"log"
"time"
"github.com/golang-jwt/jwt/v5"
)
func main() {
// 生成RSA私钥
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Fatalf("Error generating RSA key: %v", err)
}
// 创建Claims
claims := jwt.RegisteredClaims{
Issuer: "auth-service",
Subject: "user123",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
}
// 创建token
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
// 使用私钥签名
tokenString, err := token.SignedString(privateKey)
if err != nil {
log.Fatalf("Error signing token: %v", err)
}
fmt.Println("RSA signed token:", tokenString)
// 验证token
parsedToken, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
// 验证签名算法
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// 返回公钥用于验证
return &privateKey.PublicKey, nil
})
if err != nil {
log.Fatalf("Error parsing token: %v", err)
}
if claims, ok := parsedToken.Claims.(*jwt.RegisteredClaims); ok && parsedToken.Valid {
fmt.Printf("Valid token for subject: %s\n", claims.Subject)
} else {
fmt.Println("Invalid token")
}
}
安全注意事项
- 务必验证令牌的签名算法(
alg
)是否符合预期 - 使用足够强度的密钥
- 设置合理的令牌过期时间
- 不要在不安全的通道传输JWT
- 敏感信息不应放在JWT载荷中
jwt-go库提供了强大的JWT功能实现,可以满足大多数应用场景的需求。通过合理配置和正确使用,可以构建安全的认证和授权系统。
更多关于golang实现JWT生成验证与签名的全功能插件库jwt-go的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang实现JWT生成验证与签名的全功能插件库jwt-go的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang JWT 生成、验证与签名全功能指南
JWT (JSON Web Token) 是现代Web应用中常用的身份验证机制。在Go语言中,jwt-go
是最流行的JWT处理库。下面我将详细介绍如何使用jwt-go
实现JWT的生成、验证与签名功能。
1. 安装jwt-go
首先安装jwt-go库:
go get github.com/dgrijalva/jwt-go
2. 基本概念
JWT由三部分组成:
- Header:包含令牌类型和签名算法
- Payload:包含声明(claims)
- Signature:用于验证令牌完整性的签名
3. 生成JWT令牌
3.1 使用HMAC SHA256签名
package main
import (
"fmt"
"time"
"github.com/dgrijalva/jwt-go"
)
// 定义自定义claims结构体
type CustomClaims struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
jwt.StandardClaims
}
func main() {
// 定义密钥
mySigningKey := []byte("AllYourBase")
// 创建claims
claims := CustomClaims{
UserID: 12345,
Username: "john_doe",
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), // 过期时间24小时
Issuer: "test", // 签发者
},
}
// 创建token对象
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 生成签名字符串
tokenString, err := token.SignedString(mySigningKey)
if err != nil {
fmt.Println("Error generating token:", err)
return
}
fmt.Println("Generated token:", tokenString)
}
3.2 使用RSA签名
func generateTokenWithRSA() {
// 读取私钥
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(`-----BEGIN RSA PRIVATE KEY-----
...你的私钥内容...
-----END RSA PRIVATE KEY-----`))
if err != nil {
fmt.Println("Error parsing private key:", err)
return
}
// 创建claims
claims := jwt.MapClaims{
"user_id": 12345,
"exp": time.Now().Add(time.Hour * 24).Unix(),
}
// 创建并签名token
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
tokenString, err := token.SignedString(privateKey)
if err != nil {
fmt.Println("Error signing token:", err)
return
}
fmt.Println("RSA signed token:", tokenString)
}
4. 验证JWT令牌
4.1 验证HMAC签名的令牌
func validateToken(tokenString string) (*CustomClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
// 验证签名算法
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte("AllYourBase"), nil
})
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
return claims, nil
} else {
return nil, err
}
}
4.2 验证RSA签名的令牌
func validateRSAToken(tokenString string) (jwt.MapClaims, error) {
// 读取公钥
publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(`-----BEGIN PUBLIC KEY-----
...你的公钥内容...
-----END PUBLIC KEY-----`))
if err != nil {
return nil, fmt.Errorf("error parsing public key: %v", err)
}
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// 验证签名算法
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return publicKey, nil
})
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims, nil
} else {
return nil, err
}
}
5. 高级用法
5.1 刷新令牌
func refreshToken(oldToken string) (string, error) {
// 先验证旧令牌
claims, err := validateToken(oldToken)
if err != nil {
return "", err
}
// 检查令牌是否即将过期(比如剩余有效期小于15分钟)
if time.Unix(claims.ExpiresAt, 0).Sub(time.Now()) > time.Minute*15 {
return "", fmt.Errorf("token is not expired yet")
}
// 创建新claims,延长有效期
newClaims := CustomClaims{
UserID: claims.UserID,
Username: claims.Username,
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
Issuer: claims.Issuer,
},
}
// 生成新令牌
newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
return newToken.SignedString([]byte("AllYourBase"))
}
5.2 黑名单处理
var tokenBlacklist = make(map[string]bool)
func invalidateToken(tokenString string) error {
// 验证令牌
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte("AllYourBase"), nil
})
if err != nil {
return err
}
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
// 将令牌ID加入黑名单
tokenBlacklist[claims.Id] = true
return nil
}
return fmt.Errorf("invalid token")
}
func isTokenInvalid(tokenString string) bool {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte("AllYourBase"), nil
})
if err != nil {
return true
}
if claims, ok := token.Claims.(*CustomClaims); ok {
return tokenBlacklist[claims.Id]
}
return true
}
6. 最佳实践
- 密钥管理:不要将密钥硬编码在代码中,使用环境变量或密钥管理系统
- 令牌有效期:设置合理的过期时间,通常访问令牌1-2小时,刷新令牌7-30天
- HTTPS:始终通过HTTPS传输JWT
- 敏感数据:不要在JWT中存储敏感信息,因为JWT可以被解码
- 签名算法:推荐使用RS256而非HS256,因为前者可以分离验证和签名密钥
7. 总结
通过jwt-go
库,我们可以轻松实现JWT的生成、验证和签名功能。本文介绍了HMAC和RSA两种签名方式,以及令牌刷新、黑名单等高级功能。在实际应用中,请根据安全需求选择合适的签名算法,并遵循JWT的最佳实践。
对于生产环境,建议考虑使用更现代的替代库如github.com/golang-jwt/jwt
,因为dgrijalva/jwt-go
已不再维护。