gRPC with JWT

来源:https://www.cnblogs.com/lianshuiwuyi/archive/2023/08/27/17660330.html
-Advertisement-
Play Games

在 gRPC 中使用 JWT(JSON Web Tokens)進行身份驗證是一種常見的做法,它可以幫助你確保請求方的身份和許可權。下麵是一種使用 gRPC 和 JWT 進行身份驗證的步驟: 1. **生成和簽發 JWT:** 在用戶登錄成功後,你需要生成一個 JWT 並將其簽發給用戶。JWT 中可以包 ...


在 gRPC 中使用 JWT(JSON Web Tokens)進行身份驗證是一種常見的做法,它可以幫助你確保請求方的身份和許可權。下麵是一種使用 gRPC 和 JWT 進行身份驗證的步驟:

  1. 生成和簽發 JWT: 在用戶登錄成功後,你需要生成一個 JWT 並將其簽發給用戶。JWT 中可以包含一些有關用戶身份、角色、許可權等的信息。
  2. 在 gRPC 的上下文中傳遞 JWT: 當客戶端發送 gRPC 請求時,可以將 JWT 放置在 gRPC 請求的元數據(Metadata)中,作為請求的一部分。這樣,伺服器端就可以獲取 JWT 並對其進行驗證。
  3. 伺服器端驗證 JWT: 在 gRPC 服務端,你需要編寫代碼來驗證接收到的 JWT。這通常涉及到驗證 JWT 的簽名是否有效,以及檢查其中的身份信息和許可權等。
  4. 決策和授權: 根據驗證後的 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 files

server端

跟 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")[0]
	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: 50001

client

代碼如下:

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) (map[string]string, error) {
	return map[string]string{
		"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 [running]:
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"

以上示例是一個簡單的代碼示例,實際上還需要處理錯誤、安全性和其他細節。


孟斯特

聲明:本作品採用署名-非商業性使用-相同方式共用 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請註明出處。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 功能 設計一個傳送帶系統,能夠實現傳送帶的開始/停止,正轉/反轉,加減速,對傳送帶的物品計數。 按鈕/app功能控制,oled屏幕/app顯示。 設計框圖 原理圖 軟體構建階段 利用STM32CubeMX生成模板 MCU選型:STM32F103C8T6,雙擊打開。 Ststem Core->SYS: ...
  • # 背景 再很多場景中,我們可能想在子組件中修改父組件的數據,但事實上,vue不推薦我們這麼做,因為數據的修改不容易溯源。 ## Vue2寫法 在vue2中,我們使用`.sync`修飾符+自定義事件`'update:xxx'`,來使父子組件數據同步。 ```html // 父組件 我是父組件,我有{ ...
  • [系列文章目錄和關於我](https://www.cnblogs.com/cuzzz/p/16609728.html) ## 零丶引入 在[Netty源碼學習2——NioEventLoop的執行](https://www.cnblogs.com/cuzzz/p/17641482.html)中,我們學 ...
  • ## 1.1 註釋 **作用**:在代碼中加一些說明和解釋,方便自己或其他程式員程式員閱讀代碼 **兩種格式** 1. **單行註釋**:`// 描述信息` - 通常放在一行代碼的上方,或者一條語句的末尾,==對該行代碼說明== 2. **多行註釋**: `/* 描述信息 */` - 通常放在一段代 ...
  • ## 1 拉取鏡像 指定版本,在git查看相應版本,參考: https://github.com/openzipkin/zipkin 如2.21.7 ```bash docker pull openzipkin/zipkin:2.21.7 ``` ## 2 啟動 Zipkin預設埠為9411。啟動 ...
  • # Nacos集群搭建 # 1.集群結構圖 官方給出的Nacos集群圖: ![image-20210409210621117](https://img2023.cnblogs.com/blog/3014273/202308/3014273-20230827184442168-301140741.pn ...
  • 最近github上發現了一個庫(`plottable`),可以用簡單的方式就設置出花哨的 `DataFrame` 樣式。 github上的地址:[https://github.com/znstrider/plottable](https://github.com/znstrider/plottabl ...
  • Kafka 是一個基於發佈-訂閱模式的消息系統,它可以在多個生產者和消費者之間傳遞大量的數據。Kafka 的一個顯著特點是它的高吞吐率,即每秒可以處理百萬級別的消息。那麼 Kafka 是如何實現這樣高得性能呢?本文將從七個方面來分析 Kafka 的速度優勢。 - 零拷貝技術 - 僅可追加日誌結構 - ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...