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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...