如何在 Kratos 框架中集成 JWT(JSON Web Tokens)进行身份验证和授权 3 months ago
5177个字符
共有23人围观
Kratos 提供了灵活的方式来集成 JWT,通常会涉及到以下几个关键步骤和组件:
1. 引入 JWT 库:
您需要在您的 Kratos 项目中引入一个 Go 语言的 JWT 库。比较流行的选择包括:
github.com/golang-jwt/jwt/v5
: 官方推荐的 JWT 库,功能完善且维护良好。github.com/dgrijalva/jwt-go
(已归档): 一个较早的库,但现在已经归档,不建议在新项目中使用。
您可以使用 go get
命令来安装您选择的库。例如,使用官方推荐的库:
go get github.com/golang-jwt/jwt/v5
2. 配置 JWT:
您需要配置 JWT 的相关参数,例如:
- 密钥 (Secret Key): 用于签名和验证 JWT 的密钥,务必妥善保管。
- 过期时间 (Expiration Time): JWT 的有效时长。
- 签发者 (Issuer): 标识 JWT 的签发方。
- 受众 (Audience): 标识 JWT 的接收方(可选)。
- 令牌头 (Token Header) 的算法 (Algorithm): 常用的有
HS256
。
这些配置通常放在您的 Kratos 配置文件(例如 config/config.yaml
或通过环境变量)中。
jwt:
secret: "your-secret-key"
expire: 3600 # 单位:秒
issuer: "my-app"
3. 生成 JWT:
在用户登录成功后,您需要生成 JWT 并返回给客户端。这通常在您的认证服务 (Auth Service) 中完成。
import (
"time"
"github.com/golang-jwt/jwt/v5"
)
// 定义 JWT 中需要包含的 Claims
type Claims struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
jwt.RegisteredClaims
}
// 生成 JWT
func GenerateToken(userID int64, username string, secretKey string, expireDuration time.Duration, issuer string) (string, error) {
now := time.Now()
claims := Claims{
UserID: userID,
Username: username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.Add(expireDuration)),
IssuedAt: jwt.NewNumericDate(now),
NotBefore: jwt.NewNumericDate(now),
Issuer: issuer,
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signedToken, err := token.SignedString([]byte(secretKey))
if err != nil {
return "", err
}
return signedToken, nil
}
// 在您的登录处理逻辑中调用 GenerateToken
// 例如:
// token, err := GenerateToken(user.ID, user.Username, cfg.JWT.Secret, time.Duration(cfg.JWT.Expire)*time.Second, cfg.JWT.Issuer)
// if err != nil {
// // 处理错误
// }
// 返回 token 给客户端
4. 验证 JWT:
在需要进行身份验证的 API 接口中,您需要验证客户端请求中携带的 JWT。这通常通过一个 中间件 (Middleware) 来实现。
import (
"context"
"fmt"
"net/http"
"strings"
"time"
"github.com/go-kratos/kratos/v2/middleware"
"github.com/go-kratos/kratos/v2/transport"
"github.com/golang-jwt/jwt/v5"
)
// 自定义一个从上下文中获取用户信息的 Key
type UserContextKey struct{}
// JWT 验证中间件
func JWTAuth(secretKey string) middleware.Middleware {
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
if tr, ok := transport.FromServerContext(ctx); ok {
authHeader := tr.RequestHeader().Get("Authorization")
if authHeader == "" {
return nil, fmt.Errorf("missing authorization header")
}
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
return nil, fmt.Errorf("invalid authorization header format")
}
tokenString := parts[1]
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("invalid signing method")
}
return []byte(secretKey), nil
})
if err != nil {
return nil, fmt.Errorf("invalid token: %w", err)
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
// 将用户信息存储到上下文中,供后续处理使用
newCtx := context.WithValue(ctx, UserContextKey{}, claims)
return handler(newCtx, req)
}
}
return nil, fmt.Errorf("not a http request")
}
}
}
// 在您的 Service 或 Endpoint 中使用 JWTAuth 中间件
// 例如,在 gRPC 服务中:
// grpcServer := grpc.NewServer(
// grpc.Middleware(
// recovery.Recovery(),
// logging.Server(),
// jwtAuth.JWTAuth(cfg.JWT.Secret), // 应用 JWT 验证中间件
// ),
// )
// 在 HTTP 服务中:
// httpServer := http.NewServer(
// http.Middleware(
// recovery.Recovery(),
// logging.Server(),
// jwtAuth.JWTAuth(cfg.JWT.Secret), // 应用 JWT 验证中间件
// ),
// )
// 在您的 Handler 中获取用户信息
// func (s *YourService) YourMethod(ctx context.Context, req *YourRequest) (*YourResponse, error) {
// if claims, ok := ctx.Value(jwtAuth.UserContextKey{}).(*jwtAuth.Claims); ok {
// userID := claims.UserID
// username := claims.Username
// // 使用用户信息进行业务逻辑处理
// fmt.Printf("User ID: %d, Username: %s\n", userID, username)
// }
// // ...
// return &YourResponse{}, nil
// }
5. 在客户端使用 JWT:
客户端在登录成功后会收到 JWT,通常会将 JWT 存储在本地(例如 localStorage
、sessionStorage
或 Cookie 中)。在后续的 API 请求中,客户端需要将 JWT 放在请求头中,通常使用 Authorization
头,并加上 Bearer
前缀。
Authorization: Bearer <your_jwt_token>
总结关键步骤:
- 引入 JWT 库。
- 配置 JWT 相关参数(密钥、过期时间等)。
- 在用户认证成功后,使用密钥和配置生成 JWT。
- 创建一个中间件来验证请求中的 JWT。
- 在需要身份验证的路由上应用该中间件。
- (可选)将解析后的用户信息存储到上下文中,方便后续处理。
- 客户端在请求头中携带 JWT。
最佳实践和注意事项:
- 保护好您的密钥: 密钥是 JWT 安全的关键,务必使用强密钥并妥善保管,避免泄露。
- 设置合理的过期时间: 过短的过期时间会频繁要求用户重新登录,过长的过期时间会增加安全风险。
- 使用 HTTPS: 确保您的应用程序通过 HTTPS 提供服务,以防止 JWT 在传输过程中被窃取。
- 考虑使用刷新令牌 (Refresh Token): 对于需要长时间保持用户登录状态的应用,可以考虑使用刷新令牌机制来在 Access Token 过期后安全地获取新的 Access Token,而无需用户重新登录。
- 了解 JWT 的 Claims: 在 JWT 的 Payload (Claims) 中只存储必要的信息,避免泄露敏感数据。
- 处理 Token 异常: 在验证 JWT 的过程中,要妥善处理各种可能的错误情况(例如,Token 过期、无效的签名等)。
希望这些信息能够帮助您在 Kratos 中成功集成 JWT!如果您有更具体的问题或想了解某个步骤的更详细实现,请随时告诉我。