gRPC四種模式、認證和授權實戰演示,必贊~~~

Code綜藝圈發表於2021-07-13

前言

上一篇對gRPC進行簡單介紹,並通過示例體驗了一下開發過程。接下來說說實際開發常用功能,如:gRPC的四種模式、gRPC整合JWT做認證和授權等。

正文

1. gRPC四種模式服務

以下案例演示,服務端用微軟提供的模板建立,客戶端使用Winform程式演示,基於.NetCore3.1版本。具體建立步驟在上一篇說的很細了(gRPC趁現在還沒大火,搶先了解一下),接下來就直接搞重點;這裡就模仿一個學生服務,包含增、刪、改、查方法,下面是用到的proto檔案的全部內容,後續的例項就單獨標出重點即可。

syntax = "proto3"; //指定版本
// 定義名稱空間
option csharp_namespace = "Grpc.Server.Demo";
// 指定包名,避免衝突
package user;
// 定義Student 的 message型別
message Student {
  string UserName = 1;
  int32 Age=2;
  string addr = 3;
}
// 公共返回型別
message CommonResponse{
	int32 code =1;
	string msg=2;
}
// 新增學生時傳遞的型別
message AddStudentRequest{
	Student student=1;

}
// 查詢學生時傳遞的型別
message QueryStudentRequest
{
	string UserName=1;
}
// 查詢全部學生,沒有條件,但也需要一個空的message
message QueryAllStudentRequest
{
}
// 上傳圖片
message UploadImgRequest{
	bytes data = 1;
}
message StudentResponse {
  Student student =1;
}
message TokenRequest{
	string UserName=1;
	string UserPwd=2;
}
message TokenResponse{
	string Token =1;
}
// 約定需要提供的服務方法
service StudentService{
    rpc GetToken(TokenRequest) returns (TokenResponse);
	// 簡單模式,查詢
	rpc GetStudentByUserName(QueryStudentRequest) returns (StudentResponse);
	// 服務端流模式
	rpc GetAllStudent(QueryAllStudentRequest) returns (stream StudentResponse);
	// 客戶端流模式
	rpc UploadImg(stream UploadImgRequest) returns (CommonResponse);
	// 雙向流模式
	rpc AddManyStudents(stream AddStudentRequest) returns (stream StudentResponse);
}

整體的專案結構如下:

1.1 簡單模式

和現在http方式類似:客戶端發出單個請求,服務端返回單個響應。

關於簡單模式,請求引數和返回引數都是一般message型別,在上一篇中演示的模式就是簡單模式,歸納如下步驟;

服務端

  • 增加一個student.proto檔案,在檔案中定義服務,編譯自動生成對應程式碼

    定義服務格式:

    rpc 方法名(請求型別) returns (返回型別);

  • 新建StudentDemoService類,繼承生成的程式碼類,開始寫業務

  • 在Startup檔案中將服務方法暴露出去(這裡寫一次即可,後續就不重複說了)

    到這服務端就寫完啦,其實和原來寫WebApi介面一樣便捷。

客戶端

使用Winform的形式舉例演示客戶端,在建立專案時,直接選擇Winform模板即可,簡單設計了一下介面,如下:

  • 引入對應的包,將服務端的proto檔案都拷過來(服務端和客戶端proto檔案一致)

    如果編譯沒自動生成程式碼,需要檢查是否引入對應的包,是否設定了student.proto檔案的屬性,如果這塊還不瞭解,點這裡(gRPC趁現在還沒大火,搶先了解一下)先熟悉以下開發過程。

  • 在winform設計模式下,雙擊按鈕增加點選事件,開始寫業務邏輯

  • 執行看效果

    先執行服務端,在執行客戶端,輸入條件,點選簡單模式按鈕,效果如下:

    這裡查不到資料,客戶端程式報異常(別罵我程式碼寫的不嚴謹,小夥伴處理一下就好啦)

1.2 服務端流模式

客戶端發起一個請求到服務端,服務端返回連續的資料流;一般用在服務端分批返回資料的情況,客戶端能持續接收服務端的資料。

服務端

  • 在student.proto檔案中增加服務,編譯自動生成程式碼

    定義服務格式:

    rpc 方法名(請求型別) returns (stream 返回型別);

  • 在StudentDemoService類中,重寫方法寫業務邏輯程式碼

    注意點:

    1. 就算請求不需要引數,也需要一個空的message型別;
    2. 這裡返回的資料可以分批傳送,複用連線,提高效率;

客戶端

  • student.proto檔案保證和服務端一樣同步新增相應的內容

    這裡就不截圖了,小夥伴可以通過拷貝或是引用的方式保證檔案一樣就行啦

  • 在winform設計模式下,雙擊服務端流模式按鈕增加點選事件,開始寫業務邏輯

  • 執行看效果

    先執行服務端,在執行客戶端,點選服務端流模式按鈕,效果如下:

1.3 客戶端流模式

客戶端將連續的資料流傳送到服務端,服務端返回一個響應;用在客戶端傳送多次請求到服務端情況,如分段上傳圖片場景等。

服務端

  • 在student.proto檔案中增加服務,編譯自動生成程式碼

    定義服務格式:

    rpc 方法名(stream 請求型別) returns (返回型別);

  • 在StudentDemoService類中,重寫方法寫業務邏輯程式碼

客戶端

  • student.proto檔案保證和服務端一樣同步新增相應的內容

    這裡就不截圖了,小夥伴可以通過拷貝或是引用的方式保證檔案一樣就行啦。

  • 在winform設計模式下,雙擊客戶端流模式按鈕增加點選事件,開始寫業務邏輯。程式碼稍多,不截圖了,不然圖片大要失真,直接上程式碼吧。

    private async void btn_client_Click(object sender, EventArgs e)
    {
        // 用於存放選擇的檔案路徑
        string filePath = string.Empty;
        // 開啟檔案選擇對話方塊
        if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            filePath = this.openFileDialog1.FileName;
        }
        if(string.IsNullOrEmpty(filePath))
        {
            this.txt_result.Text = "請選擇檔案";
            return;
        }
        //1、建立grpc客戶端
        using var channel = GrpcChannel.ForAddress("https://localhost:5001");
        var grpcClient = new StudentService.StudentServiceClient(channel);
        //2、讀取選擇的檔案
        FileStream fileStream = File.OpenRead(filePath);
        //3、通過客戶端請求流將檔案流傳送的服務端
        using var call = grpcClient.UploadImg();
        var clientStream = call.RequestStream;
        //4、迴圈傳送,指定傳送完檔案
        while(true)
        {
            // 一次最多傳送1024位元組
            byte[] buffer = new byte[1024];
            int nRead = await fileStream.ReadAsync(buffer, 0, buffer.Length);
            // 直到讀不到資料為止,即檔案已經傳送完成,即退出傳送
            if(nRead==0)
            {
                break;
            }
            // 5、將每次讀取到的檔案流通過客戶端流傳送到服務端
            await clientStream.WriteAsync(new UploadImgRequest { Data = ByteString.CopyFrom(buffer) });
        }
        // 6、傳送完成之後,告訴服務端傳送完成
        await clientStream.CompleteAsync();
        // 7、接收返回結果,並顯示在文字框中
        var res = await call.ResponseAsync;
        this.txt_result.Text = $"上傳返回Code:{res.Code},Msg:{res.Msg}";
    }
    
  • 執行看效果

    先執行服務端,在執行客戶端,點選客戶端流模式按鈕,效果如下:

    在彈框中選擇一個jpg的圖片(因為方便演示,服務端固定寫為jpg了),如下:

    選擇完圖片就開始上傳了,如下:

    是不是已經感覺到gRPC的優點了,資料傳輸量及方式有沒有先進一點。

1.4 雙向流模式

雙向流就是服務端流和客戶端流的整合,請求和返回都可以通過流的方式互動。

服務端

  • 在student.proto檔案中增加服務,編譯自動生成程式碼

    定義服務格式:

    rpc 方法名(stream 請求型別) returns (stream 返回型別);

  • 在StudentDemoService類中,重寫方法寫業務邏輯程式碼

客戶端

  • student.proto檔案保證和服務端一樣同步新增相應的內容

    這裡就不截圖了,小夥伴可以通過拷貝或是引用的方式保證檔案一樣就行啦

  • 在winform設計模式下,雙擊雙向流模式按鈕增加點選事件,開始寫業務邏輯。

  • 執行看效果

    先執行服務端,在執行客戶端,點選雙向流模式按鈕,效果如下:

    點選服務端流按鈕檢視全部數,看看是否新增成功:

gRPC的四種模式就簡單介紹這麼多,小夥伴可以根據提供的思路應用到專案中。 在演示案例中是不是感受到gRPC相比WebApi有更多的選擇和優勢,另外通過服務端的控制檯可以看到,互動使用的是HTTP/2協議,小夥伴感興趣可以去了解一下:

2. gRPC整合JWT認證

和WebAPI一樣,如果提供的服務介面“裸奔”,那麼風險是很大的;關於這點,之前針對WebApi認證和授權分享了兩篇文章,傳送門在這(跟我一起學.NetCore之WebApi介面裸奔有風險(Jwt)跟我一起學.NetCore之熟悉的介面許可權驗證不能少(Jwt))。

其實gRPC整合JWT做認證授權,和原來WebApi整合方式差不多一樣,太細節的描述小夥伴可以查閱上面兩篇文章;接下來的需求目的就是把上面提供的服務保護起來,只有認證通過才能呼叫。

2.1 引入Jwt相關包,註冊服務,中介軟體管道增加認證流程
  • 在gRPC服務端專案中引入Jwt相關包

    Microsoft.AspNetCore.Authentication.JwtBearer

  • 在Startup檔案中註冊相關服務

  • 在Startup檔案中增加認證流程

  • 在服務上增加Authorize特性標識

    執行看效果:

    通過呼叫結果得知,現在提供的gRPC服務已經受到保護,需要持有對應身份的請求才能訪問,所以接下來就需要服務端提供一個獲取身份Token的方法。

2.2 服務端增加獲取Token的服務方法
  • 服務端增加獲取Token的服務方法

    現在student.proto檔案中增加獲取Token的相關約定,編譯自動生成對應程式碼,如下:

    重寫方法,編寫獲取Token的邏輯,如下:

    生成token的核心邏輯為方法GenerateToken,如下:

    到這,服務端生成Token的方法就搞定了,接下開始讓客戶端獲取並使用即可。

2.3 客戶端獲取Token並使用
  • 確保student.proto檔案內容和服務端的一致,然後編譯自動生成程式碼。

    這裡就不截圖了,小夥伴可以通過拷貝或是引用的方式保證檔案一樣就行啦

  • 這裡將服務端流模式的按鈕複製一個出來,在其點選事件中增加帶Token的邏輯

    獲取Token的邏輯就是簡單的呼叫服務端的方法,如下:

    這樣就完成Jwt的整合了,服務端、客戶端都執行起來看一下:

到這裡,gRPC整合Jwt做認證的全部演示就完成了,除了呼叫方式和客戶端傳遞Token的方式不一樣,其他的都和WebApi使用方式一樣。

重點:這裡gRPC可以通過Metadata進行傳遞資料,和之前WebApi的請求頭很像,可以根據自己需求進行任意封裝傳遞。

3. gRPC許可權驗證思路提一下

上面只是進行了認證,還沒有對服務方法許可權進行管控,只要認證通過就能呼叫全部服務方法;在實際應用場景中更希望的是分配啥許可權才能呼叫對應的服務,所以許可權管控少不了。

gRPC的許可權管控和WebApi的管控方式一樣,同樣可以使用策略的方式進行許可權驗證。gRPC同樣能獲取到請求方法的Url(包名.服務名.方法名),這樣就可以以這種規則進行許可權配置,然後驗證即可。詳細步驟可以參考跟我一起學.NetCore之熟悉的介面許可權驗證不能少(Jwt),接下來就來說說主要步驟:

3.1 使用動態許可權策略形式
  • 增加一個PermissionRequirement.cs檔案,如下:

  • 增加一個PermissionHandler.cs檔案,整合自AuthorizationHandler,然後重寫處理方法,核心程式碼如下:

  • Startup.cs檔案中註冊相關服務,如下:

  • 在Authorize特性中傳遞對應的策略名稱,如下:

  • 這裡模擬配置許可權,在獲取token方法中內建幾條對應使用者的許可權資料

  • 執行看效果,如下:

    最後驗證成功就正常返回結果,如果驗證不成功就返回失敗,客戶端就報異常,如下:

原始碼地址:https://gitee.com/CodeZoe/g-rpc/tree/master

後續會把其他程式碼也整理到碼雲上。

總結

關於gRPC實際應用場景常用的功能就先說到這吧,以上案例演示只是提供思路,小夥伴使用時可以根據對應的需求進行擴充套件和處理。

既然聊到了服務間通訊,分散式事務肯定是避不開的,下一篇開始說說分散式事務相關的點。

一個被程式搞醜的帥小夥,關注"Code綜藝圈",和我一起學~~~

相關文章