梦见你的名字 发表于 2023-9-1 19:41:53

gRPC with JWT

在 gRPC 中使用 JWT(JSON Web Tokens)进行身份验证是一种常见的做法,它可以帮助你确保请求方的身份和权限。下面是一种使用 gRPC 和 JWT 进行身份验证的步骤:

[*]生成和签发 JWT: 在用户登录成功后,你需要生成一个 JWT 并将其签发给用户。JWT 中可以包含一些有关用户身份、角色、权限等的信息。
[*]在 gRPC 的上下文中传递 JWT: 当客户端发送 gRPC 请求时,可以将 JWT 放置在 gRPC 请求的元数据(Metadata)中,作为请求的一部分。这样,服务器端就可以获取 JWT 并对其进行验证。
[*]服务器端验证 JWT: 在 gRPC 服务端,你需要编写代码来验证接收到的 JWT。这通常涉及到验证 JWT 的签名是否有效,以及检查其中的身份信息和权限等。
[*]决策和授权: 根据验证后的 JWT 信息,你可以决定是否允许用户继续访问请求的资源。这可能涉及到一些授权策略和业务逻辑。
以下是一个简单的示例,展示如何在 gRPC 中使用 JWT 进行身份验证:
proto文件

内容如下:
syntax = "proto3";

package chaincode.pb;

option go_package = "./;pb";

message HelloRequest { string name = 1; }
message HelloResponse { string reply = 2; }

service SayHi { rpc Hi(HelloRequest) returns (HelloResponse); }通过下面的命令生成相关的文件:
$ protoc --go_out=./ --go-grpc_out=./ example.proto
$ tree
.
├── example_grpc.pb.go
├── example.pb.go
└── example.proto

0 directories, 3 filesserver端

跟 client 端约定内容如下:

[*]token有效期为半小时
[*]iss使用gRPC token
[*]sub使用gRPC example server
代码如下:
package main

import (
        "chaincode/pb"
        "context"
        "fmt"
        "net"
        "time"

        "github.com/golang-jwt/jwt/v5"
        "github.com/pkg/errors"
        "google.golang.org/grpc"
        "google.golang.org/grpc/metadata"
)

var testKey = "testKey"

type HiService struct {
        pb.UnimplementedSayHiServer
}

func verifyToken(tokenString string) error {
        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
                return []byte(testKey), nil
        })
        if err != nil {
                return errors.Wrap(err, "init token parser error")
        }
        if !token.Valid {
                return errors.New("invalid token")
        }
        claims, ok := token.Claims.(jwt.MapClaims)
        if !ok {
                return errors.New("invalid claims")
        }
        exp, err := claims.GetExpirationTime()
        if err != nil {
                return errors.Wrap(err, "GetExpirationTime from token error")
        }

        now := time.Now()
        if now.Sub(exp.Time) > 0 {
                return errors.New("the token expires")
        }
        if claims["sub"] != "gRPC example server" {
                return errors.New("invalid sub")
        }
        if claims["iss"] != "gRPC token" {
                return errors.New("invalid iss")
        }
        return nil
}

func (s *HiService) Hi(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {

        md, ok := metadata.FromIncomingContext(ctx)
        if !ok {
                return nil, errors.New("token信息获取失败")
        }
        token := md.Get("Authorization")
        if err := verifyToken(token); err != nil {
                return nil, errors.Wrap(err, "token验证失败")
        }

        return &pb.HelloResponse{Reply: "hello " + req.Name}, nil
}

func main() {
        // 创建grpc服务示例
        sv := grpc.NewServer()
        // 注册我们的服务
        pb.RegisterSayHiServer(sv, new(HiService))

        // 绑定端口,提供服务
        lis, err := net.Listen("tcp", ":50001")
        if err != nil {
                panic(err)
        }
        // 启动服务
        fmt.Println("liston on: 50001")
        sv.Serve(lis)
}$ go run main.go
liston on: 50001client

代码如下:
package main

import (
        "chaincode/pb"
        "context"
        "fmt"
        "time"

        "github.com/golang-jwt/jwt/v5"
        "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
)

var testKey = "testKey"

func genToken() (string, error) {
        claims := jwt.RegisteredClaims{
                ExpiresAt: jwt.NewNumericDate(time.Now().Add(30 * time.Minute)),
                Issuer:    "gRPC token",
                Subject:   "gRPC example client",
        }
        token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        return token.SignedString([]byte(testKey))
}

type TokeAuth struct {
        Token string
}

func (t *TokeAuth) GetRequestMetadata(ctx context.Context, uri ...string) (mapstring, error) {
        return mapstring{
                "Authorization": t.Token,
        }, nil
}

func (t *TokeAuth) RequireTransportSecurity() bool {
        return false
}

func main() {
        token, err := genToken()
        if err != nil {
                panic(err)
        }
        conn, err := grpc.Dial("localhost:50001", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(&TokeAuth{Token: token}))
        if err != nil {
                panic(err)
        }
        defer conn.Close()

        client := pb.NewSayHiClient(conn)

        resp, err := client.Hi(context.Background(), &pb.HelloRequest{Name: "Wang"})
        if err != nil {
                panic(err)
        }
        fmt.Println(resp.String())
}现在我们先将 client 端生成 token 的sub 设置为 gRPC example client,执行
$ go run main.go
panic: rpc error: code = Unknown desc = token验证失败: invalid sub

goroutine 1 :
main.main()
      /root/go/src/example/client/main.go:55 +0x2f2
exit status 2再将 client 端生成 token 的sub 设置为 gRPC example server,执行
$ go run main.go
reply:"hello Wang"以上示例是一个简单的代码示例,实际上还需要处理错误、安全性和其他细节。
https://img2023.cnblogs.com/blog/1007709/202308/1007709-20230810162948167-1526955652.jpg声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: gRPC with JWT