给kratos项目添加jwt auth认证功能 2 months ago
博客大纲
为 Kratos 项目添加 JWT 认证功能
下面我将详细介绍如何在 Kratos 项目中集成 JWT 认证功能,包括服务端和客户端的完整实现。
1. 安装所需依赖
首先安装 JWT 相关包:
go get github.com/golang-jwt/jwt/v4
2. 配置 JWT 参数
在 configs/config.yaml
中添加 JWT 配置:
server:
http:
addr: 0.0.0.0:8000
grpc:
addr: 0.0.0.0:9000
jwt:
secret: "your-256-bit-secret" # 推荐至少32字符
expires: 720h # 30天过期
对应的配置文件结构 internal/conf/conf.proto
:
message Bootstrap {
Server server = 1;
Data data = 2;
Jwt jwt = 3;
}
message Jwt {
string secret = 1;
string expires = 2;
}
3. 实现 JWT 工具类
创建 internal/pkg/jwt/jwt.go
:
package jwt
import (
"time"
"github.com/golang-jwt/jwt/v4"
)
type Claims struct {
UserID int64 `json:"user_id"`
jwt.RegisteredClaims
}
func GenerateToken(secret string, expires time.Duration, userID int64) (string, error) {
claims := Claims{
UserID: userID,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(expires)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}
func ParseToken(secret, tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, err
}
4. 实现认证中间件
创建 internal/middleware/auth/auth.go
:
package auth
import (
"context"
"strings"
"github.com/go-kratos/kratos/v2/errors"
"github.com/go-kratos/kratos/v2/middleware"
"github.com/go-kratos/kratos/v2/transport/http"
"your_project/pkg/jwt"
)
const (
authorizationKey = "Authorization"
bearerWord = "Bearer"
)
func Auth(jwtSecret string) middleware.Middleware {
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (interface{}, error) {
if tr, ok := transport.FromServerContext(ctx); ok {
if ht, ok := tr.(*http.Transport); ok {
authHeader := ht.Request().Header.Get(authorizationKey)
if authHeader == "" {
return nil, errors.Unauthorized("UNAUTHORIZED", "Authorization header is missing")
}
token := strings.TrimPrefix(authHeader, bearerWord+" ")
claims, err := jwt.ParseToken(jwtSecret, token)
if err != nil {
return nil, errors.Unauthorized("UNAUTHORIZED", err.Error())
}
// 将用户ID存入context
ctx = context.WithValue(ctx, "user_id", claims.UserID)
}
}
return handler(ctx, req)
}
}
}
5. 在服务中应用中间件
在 internal/server/http.go
中:
import (
"your_project/internal/middleware/auth"
)
func NewHTTPServer(c *conf.Server, jwtConf *conf.Jwt, user *service.UserService) *http.Server {
opts := []http.ServerOption{
http.Middleware(
auth.Auth(jwtConf.Secret), // 添加JWT认证中间件
),
}
if c.Http.Network != "" {
opts = append(opts, http.Network(c.Http.Network))
}
if c.Http.Addr != "" {
opts = append(opts, http.Address(c.Http.Addr))
}
if c.Http.Timeout != nil {
opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
}
srv := http.NewServer(opts...)
v1.RegisterUserHTTPServer(srv, user)
return srv
}
6. 实现登录接口
在用户服务中:
func (s *UserService) Login(ctx context.Context, req *pb.LoginRequest) (*pb.LoginReply, error) {
// 验证用户名密码
user, err := s.repo.VerifyPassword(ctx, req.Username, req.Password)
if err != nil {
return nil, err
}
// 生成JWT
token, err := jwt.GenerateToken(s.jwt.Secret, s.jwt.Expires, user.ID)
if err != nil {
return nil, err
}
return &pb.LoginReply{
Token: token,
}, nil
}
7. 客户端使用 JWT
HTTP 客户端:
func main() {
conn, err := http.NewClient(
context.Background(),
http.WithMiddleware(
clientMiddleware.JWT("your_token_here"),
),
http.WithEndpoint("localhost:8000"),
)
// ...
}
gRPC 客户端:
func main() {
conn, err := grpc.DialInsecure(
context.Background(),
grpc.WithEndpoint("localhost:9000"),
grpc.WithMiddleware(
grpcMiddleware.JWT("your_token_here"),
),
)
// ...
}
8. 可选 - 排除特定路由
如果需要某些路由不需要认证,可以修改中间件:
func Auth(jwtSecret string, whiteList []string) middleware.Middleware {
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (interface{}, error) {
// 检查是否在白名单
if tr, ok := transport.FromServerContext(ctx); ok {
if ht, ok := tr.(*http.Transport); ok {
for _, path := range whiteList {
if ht.Request().URL.Path == path {
return handler(ctx, req)
}
}
}
}
// 原有认证逻辑...
}
}
}
总结
通过以上步骤,你已经为 Kratos 项目添加了完整的 JWT 认证功能:
- 配置 JWT 参数
- 实现 JWT 生成和解析工具
- 创建认证中间件
- 在服务中集成中间件
- 实现登录接口
- 客户端使用 JWT
这种实现方式既保持了 Kratos 的中间件风格,又提供了灵活的认证机制。你可以根据需要调整过期时间、密钥轮换等安全策略。