gRPC四种模式、认证和授权实战演示
前言
上一篇對gRPC進行簡單介紹,并通過示例體驗了一下開發(fā)過程。接下來說說實際開發(fā)常用功能,如:gRPC的四種模式、gRPC集成JWT做認證和授權(quán)等。
正文
1. gRPC四種模式服務(wù)
以下案例演示,服務(wù)端用微軟提供的模板創(chuàng)建,客戶端使用Winform程序演示,基于.NetCore3.1版本。具體創(chuàng)建步驟在上一篇說的很細了(gRPC趁現(xiàn)在還沒大火,搶先了解一下),接下來就直接搞重點;這里就模仿一個學(xué)生服務(wù),包含增、刪、改、查方法,下面是用到的proto文件的全部內(nèi)容,后續(xù)的實例就單獨標(biāo)出重點即可。
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; } //?添加學(xué)生時傳遞的類型 message?AddStudentRequest{Student?student=1;} //?查詢學(xué)生時傳遞的類型 message?QueryStudentRequest {string?UserName=1; } //?查詢?nèi)繉W(xué)生,沒有條件,但也需要一個空的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; } //?約定需要提供的服務(wù)方法 service?StudentService{rpc?GetToken(TokenRequest)?returns?(TokenResponse);//?簡單模式,查詢rpc?GetStudentByUserName(QueryStudentRequest)?returns?(StudentResponse);//?服務(wù)端流模式rpc?GetAllStudent(QueryAllStudentRequest)?returns?(stream?StudentResponse);//?客戶端流模式rpc?UploadImg(stream?UploadImgRequest)?returns?(CommonResponse);//?雙向流模式rpc?AddManyStudents(stream?AddStudentRequest)?returns?(stream?StudentResponse); }整體的項目結(jié)構(gòu)如下:
1.1 簡單模式
和現(xiàn)在http方式類似:客戶端發(fā)出單個請求,服務(wù)端返回單個響應(yīng)。
關(guān)于簡單模式,請求參數(shù)和返回參數(shù)都是一般message類型,在上一篇中演示的模式就是簡單模式,歸納如下步驟;
服務(wù)端
增加一個student.proto文件,在文件中定義服務(wù),編譯自動生成對應(yīng)代碼
定義服務(wù)格式:
rpc 方法名(請求類型) returns (返回類型);
新建StudentDemoService類,繼承生成的代碼類,開始寫業(yè)務(wù)
在Startup文件中將服務(wù)方法暴露出去(這里寫一次即可,后續(xù)就不重復(fù)說了)
到這服務(wù)端就寫完啦,其實和原來寫WebApi接口一樣便捷。
客戶端
使用Winform的形式舉例演示客戶端,在創(chuàng)建項目時,直接選擇Winform模板即可,簡單設(shè)計了一下界面,如下:
引入對應(yīng)的包,將服務(wù)端的proto文件都拷過來(服務(wù)端和客戶端proto文件一致)
如果編譯沒自動生成代碼,需要檢查是否引入對應(yīng)的包,是否設(shè)置了student.proto文件的屬性,如果這塊還不了解,點這里(gRPC趁現(xiàn)在還沒大火,搶先了解一下)先熟悉以下開發(fā)過程。
在winform設(shè)計模式下,雙擊按鈕增加點擊事件,開始寫業(yè)務(wù)邏輯
運行看效果
先運行服務(wù)端,在運行客戶端,輸入條件,點擊簡單模式按鈕,效果如下:
這里查不到數(shù)據(jù),客戶端程序報異常(別罵我代碼寫的不嚴謹,小伙伴處理一下就好啦)
1.2 服務(wù)端流模式
客戶端發(fā)起一個請求到服務(wù)端,服務(wù)端返回連續(xù)的數(shù)據(jù)流;一般用在服務(wù)端分批返回數(shù)據(jù)的情況,客戶端能持續(xù)接收服務(wù)端的數(shù)據(jù)。
服務(wù)端
在student.proto文件中增加服務(wù),編譯自動生成代碼
定義服務(wù)格式:
rpc 方法名(請求類型) returns (stream 返回類型);
在StudentDemoService類中,重寫方法寫業(yè)務(wù)邏輯代碼
注意點:
就算請求不需要參數(shù),也需要一個空的message類型;
這里返回的數(shù)據(jù)可以分批發(fā)送,復(fù)用連接,提高效率;
客戶端
student.proto文件保證和服務(wù)端一樣同步添加相應(yīng)的內(nèi)容
這里就不截圖了,小伙伴可以通過拷貝或是引用的方式保證文件一樣就行啦
在winform設(shè)計模式下,雙擊服務(wù)端流模式按鈕增加點擊事件,開始寫業(yè)務(wù)邏輯
運行看效果
先運行服務(wù)端,在運行客戶端,點擊服務(wù)端流模式按鈕,效果如下:
1.3 客戶端流模式
客戶端將連續(xù)的數(shù)據(jù)流發(fā)送到服務(wù)端,服務(wù)端返回一個響應(yīng);用在客戶端發(fā)送多次請求到服務(wù)端情況,如分段上傳圖片場景等。
服務(wù)端
在student.proto文件中增加服務(wù),編譯自動生成代碼
定義服務(wù)格式:
rpc 方法名(stream 請求類型) returns (返回類型);
在StudentDemoService類中,重寫方法寫業(yè)務(wù)邏輯代碼
客戶端
student.proto文件保證和服務(wù)端一樣同步添加相應(yīng)的內(nèi)容
這里就不截圖了,小伙伴可以通過拷貝或是引用的方式保證文件一樣就行啦。
在winform設(shè)計模式下,雙擊客戶端流模式按鈕增加點擊事件,開始寫業(yè)務(wù)邏輯。代碼稍多,不截圖了,不然圖片大要失真,直接上代碼吧。
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、創(chuàng)建grpc客戶端using?var?channel?=?GrpcChannel.ForAddress("https://localhost:5001");var?grpcClient?=?new?StudentService.StudentServiceClient(channel);//2、讀取選擇的文件FileStream?fileStream?=?File.OpenRead(filePath);//3、通過客戶端請求流將文件流發(fā)送的服務(wù)端using?var?call?=?grpcClient.UploadImg();var?clientStream?=?call.RequestStream;//4、循環(huán)發(fā)送,指定發(fā)送完文件while(true){//?一次最多發(fā)送1024字節(jié)byte[]?buffer?=?new?byte[1024];int?nRead?=?await?fileStream.ReadAsync(buffer,?0,?buffer.Length);//?直到讀不到數(shù)據(jù)為止,即文件已經(jīng)發(fā)送完成,即退出發(fā)送if(nRead==0){break;}//?5、將每次讀取到的文件流通過客戶端流發(fā)送到服務(wù)端await?clientStream.WriteAsync(new?UploadImgRequest?{?Data?=?ByteString.CopyFrom(buffer)?});}//?6、發(fā)送完成之后,告訴服務(wù)端發(fā)送完成await?clientStream.CompleteAsync();//?7、接收返回結(jié)果,并顯示在文本框中var?res?=?await?call.ResponseAsync;this.txt_result.Text?=?$"上傳返回Code:{res.Code},Msg:{res.Msg}"; }運行看效果
先運行服務(wù)端,在運行客戶端,點擊客戶端流模式按鈕,效果如下:
在彈框中選擇一個jpg的圖片(因為方便演示,服務(wù)端固定寫為jpg了),如下:
選擇完圖片就開始上傳了,如下:
是不是已經(jīng)感覺到gRPC的優(yōu)點了,數(shù)據(jù)傳輸量及方式有沒有先進一點。
1.4 雙向流模式
雙向流就是服務(wù)端流和客戶端流的整合,請求和返回都可以通過流的方式交互。
服務(wù)端
在student.proto文件中增加服務(wù),編譯自動生成代碼
定義服務(wù)格式:
rpc 方法名(stream 請求類型) returns (stream 返回類型);
在StudentDemoService類中,重寫方法寫業(yè)務(wù)邏輯代碼
客戶端
student.proto文件保證和服務(wù)端一樣同步添加相應(yīng)的內(nèi)容
這里就不截圖了,小伙伴可以通過拷貝或是引用的方式保證文件一樣就行啦
在winform設(shè)計模式下,雙擊雙向流模式按鈕增加點擊事件,開始寫業(yè)務(wù)邏輯。
運行看效果
先運行服務(wù)端,在運行客戶端,點擊雙向流模式按鈕,效果如下:
點擊服務(wù)端流按鈕查看全部數(shù),看看是否添加成功:
gRPC的四種模式就簡單介紹這么多,小伙伴可以根據(jù)提供的思路應(yīng)用到項目中。在演示案例中是不是感受到gRPC相比WebApi有更多的選擇和優(yōu)勢,另外通過服務(wù)端的控制臺可以看到,交互使用的是HTTP/2協(xié)議,小伙伴感興趣可以去了解一下:
2. gRPC集成JWT認證
和WebAPI一樣,如果提供的服務(wù)接口“裸奔”,那么風(fēng)險是很大的;關(guān)于這點,之前針對WebApi認證和授權(quán)分享了兩篇文章,傳送門在這(跟我一起學(xué).NetCore之WebApi接口裸奔有風(fēng)險(Jwt)、跟我一起學(xué).NetCore之熟悉的接口權(quán)限驗證不能少(Jwt))。
其實gRPC集成JWT做認證授權(quán),和原來WebApi集成方式差不多一樣,太細節(jié)的描述小伙伴可以查閱上面兩篇文章;接下來的需求目的就是把上面提供的服務(wù)保護起來,只有認證通過才能調(diào)用。
2.1 引入Jwt相關(guān)包,注冊服務(wù),中間件管道增加認證流程
在gRPC服務(wù)端項目中引入Jwt相關(guān)包
Microsoft.AspNetCore.Authentication.JwtBearer
在Startup文件中注冊相關(guān)服務(wù)
在Startup文件中增加認證流程
在服務(wù)上增加Authorize特性標(biāo)識
運行看效果:
通過調(diào)用結(jié)果得知,現(xiàn)在提供的gRPC服務(wù)已經(jīng)受到保護,需要持有對應(yīng)身份的請求才能訪問,所以接下來就需要服務(wù)端提供一個獲取身份Token的方法。
2.2 服務(wù)端增加獲取Token的服務(wù)方法
服務(wù)端增加獲取Token的服務(wù)方法
現(xiàn)在student.proto文件中增加獲取Token的相關(guān)約定,編譯自動生成對應(yīng)代碼,如下:
重寫方法,編寫獲取Token的邏輯,如下:
生成token的核心邏輯為方法GenerateToken,如下:
到這,服務(wù)端生成Token的方法就搞定了,接下開始讓客戶端獲取并使用即可。
2.3 客戶端獲取Token并使用
確保student.proto文件內(nèi)容和服務(wù)端的一致,然后編譯自動生成代碼。
這里就不截圖了,小伙伴可以通過拷貝或是引用的方式保證文件一樣就行啦
這里將服務(wù)端流模式的按鈕復(fù)制一個出來,在其點擊事件中增加帶Token的邏輯
獲取Token的邏輯就是簡單的調(diào)用服務(wù)端的方法,如下:
這樣就完成Jwt的集成了,服務(wù)端、客戶端都運行起來看一下:
到這里,gRPC集成Jwt做認證的全部演示就完成了,除了調(diào)用方式和客戶端傳遞Token的方式不一樣,其他的都和WebApi使用方式一樣。
重點:這里gRPC可以通過Metadata進行傳遞數(shù)據(jù),和之前WebApi的請求頭很像,可以根據(jù)自己需求進行任意封裝傳遞。
3. gRPC權(quán)限驗證思路提一下
上面只是進行了認證,還沒有對服務(wù)方法權(quán)限進行管控,只要認證通過就能調(diào)用全部服務(wù)方法;在實際應(yīng)用場景中更希望的是分配啥權(quán)限才能調(diào)用對應(yīng)的服務(wù),所以權(quán)限管控少不了。
gRPC的權(quán)限管控和WebApi的管控方式一樣,同樣可以使用策略的方式進行權(quán)限驗證。gRPC同樣能獲取到請求方法的Url(包名.服務(wù)名.方法名),這樣就可以以這種規(guī)則進行權(quán)限配置,然后驗證即可。詳細步驟可以參考跟我一起學(xué).NetCore之熟悉的接口權(quán)限驗證不能少(Jwt),接下來就來說說主要步驟:
3.1 使用動態(tài)權(quán)限策略形式
增加一個PermissionRequirement.cs文件,如下:
增加一個PermissionHandler.cs文件,集成自AuthorizationHandler,然后重寫處理方法,核心代碼如下:
Startup.cs文件中注冊相關(guān)服務(wù),如下:
在Authorize特性中傳遞對應(yīng)的策略名稱,如下:
這里模擬配置權(quán)限,在獲取token方法中內(nèi)置幾條對應(yīng)用戶的權(quán)限數(shù)據(jù)
運行看效果,如下:
最后驗證成功就正常返回結(jié)果,如果驗證不成功就返回失敗,客戶端就報異常,如下:
源代碼地址:https://gitee.com/CodeZoe/g-rpc/tree/master
后續(xù)會把其他代碼也整理到碼云上。
總結(jié)
關(guān)于gRPC實際應(yīng)用場景常用的功能就先說到這吧,以上案例演示只是提供思路,小伙伴使用時可以根據(jù)對應(yīng)的需求進行擴展和處理。
既然聊到了服務(wù)間通信,分布式事務(wù)肯定是避不開的,下一篇開始說說分布式事務(wù)相關(guān)的點。
一個被程序搞丑的帥小伙,關(guān)注"Code綜藝圈",和我一起學(xué)~~~
總結(jié)
以上是生活随笔為你收集整理的gRPC四种模式、认证和授权实战演示的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET 6 即将到来的新特性 — 隐式
- 下一篇: .net core 微服务下的手工签名实