gRPC四種模式、認證和授權實戰演示,必贊~~~
前言
上一篇對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類中,重寫方法寫業務邏輯代碼
注意點:
- 就算請求不需要參數,也需要一個空的message類型;
- 這裡返回的數據可以分批發送,復用連接,提高效率;
客戶端
-
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("//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方法中內置幾條對應用戶的權限數據
-
運行看效果,如下:
最後驗證成功就正常返回結果,如果驗證不成功就返回失敗,客戶端就報異常,如下:
源代碼地址://gitee.com/CodeZoe/g-rpc/tree/master
後續會把其他代碼也整理到碼雲上。
總結
關於gRPC實際應用場景常用的功能就先說到這吧,以上案例演示只是提供思路,小夥伴使用時可以根據對應的需求進行擴展和處理。
既然聊到了服務間通信,分佈式事務肯定是避不開的,下一篇開始說說分佈式事務相關的點。
一個被程序搞丑的帥小伙,關注”Code綜藝圈”,和我一起學~~~