.net core gRPC與IdentityServer4整合認證授權
阿新 • • 發佈:2019-10-22
前言
隨著.net core3.0的正式釋出,gRPC服務被整合到了VS2019。本文主要演示如何對gRPC的服務進行認證授權。
分析
目前.net core使用最廣的認證授權元件是基於OAuth2.0協議的IdentityServer4。而gRPC可以與ASP.NET Core Authentication一起使用來實現認證授權功能。本文將建立3個應用程式來完成gRPC的認證授權演示過程。
步驟
Ids4.Server
1.建立一個.net core的webapi
2.nuget引用最新的IdentityServer4的包
<PackageReference Include="IdentityServer4" Version="3.0.1" />
IdentityServer4相關配置,因為是演示所以很簡單,生產場景大家根據實際情況配置。
namespace Ids4.Server { public class Config { public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email(), }; } public static IEnumerable<ApiResource> GetApis() { return new List<ApiResource> { new ApiResource("api", "Demo API") { ApiSecrets = { new Secret("secret".Sha256()) } } }; } public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId = "client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "api" }, }, }; } } }
4. startup.cs 注入服務
services.AddIdentityServer().AddInMemoryApiResources(Config.GetApis())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryClients(Config.GetClients())
.AddDeveloperSigningCredential(persistKey: false);
5. startup.cs 配置http請求管道
app.UseIdentityServer();
6. 啟動服務,使用PostMan進行除錯,有返回結果表示服務建立成功
POST /connect/token HTTP/1.1
Host: localhost:5000
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=client1&client_secret=secret
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IlVyMmxuM2EwNGhWaGdDdWZTVTNtZVEiLCJ0eXAiOiJhdCtqd3QifQ.eyJuYmYiOjE1NzEzMDkwMTMsImV4cCI6MTU3MTMxMjYxMywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoiYXBpIiwiY2xpZW50X2lkIjoiY2xpZW50Iiwic2NvcGUiOlsiYXBpIl19.X4pg9_FbPbWZl814XC0NYWTslfhMG4aXWEyXLrXhIojPJaL7Qvq9ieDF4S7x0psRcClwbwCg81hTrG3j2Cmcl0nzj_Ic7UY8MfN0dvAuy_fJdUf76TX0oOpir3SxgC8gnfaKyEoWmmbIyvwicWbKp9PP-EeTxG6-oMYn6PO22cwRVHDD28ZdEAq2DEkATOh9XPavoi9vGZhPQ1nviKL1K6tcYUGXSQbhWI9ISEqnTHqMX1xA_gcDIAplGvquXmtXdgyTsRoGolEtzDAYVH4sGUb1SpYx2nc8bgl6Qw27fhe0Uy9MR70kQMcEkCTdXLivjYjkuI9_quUyJHzdi5KgnQ",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "api"
}
本篇不對IdentityServer4做更多的講解,大家可以參考官方文件瞭解更多。
Grpc.Server
1. 使用vs2019建立gRPC服務端。
2. 不用做任何更改,直接使用預設建立的gRPC服務
Grpc.Client
1. 建立一個控制檯程式
2. 引入nuget安裝包
<PackageReference Include="Google.Protobuf" Version="3.10.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.23.2" />
<PackageReference Include="Grpc.Tools" Version="2.24.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
這3個核心包是客戶端必備的,其中grpc.tools幫我們把proto檔案轉化成C#程式碼。
3. 建立Protos資料夾
4. 複製Grpc.Server專案Protos資料夾下的greet.proto檔案到本專案的Protos資料夾
5. greet.proto檔案右鍵設定gGRC Stub Classes為Client only。
也可以直接使用在專案檔案裡面程式碼設定如下:
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>
6. gRPC客戶端訪問服務端程式碼
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var response = client.SayHello(new HelloRequest { Name = "World" });
Console.WriteLine(response.Message);
啟動gRPC服務端,在啟動gRPC客戶端控制檯列印hello word表示成功。
identityServer接入gRPC是非常容易,和傳統webapi差不多。
改造Grpc.Server支援IdentityServer4
1. 引入nuget包
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
2. startup.cs 注入服務,和IdentityServer4一樣。
services.AddGrpc(x => x.EnableDetailedErrors = false);
services.AddAuthorization();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
});
3. startup.cs 配置http請求管道
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>();
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
});
});
4. 對需要授權的服務打標籤[Authorize],可以打在類上也可以打在方法上
[Authorize]
public class GreeterService : Greeter.GreeterBase
{
}
這個時候我們啟動Grpc.Client訪問Grpc.Server服務
發現報錯401。說明此服務需要攜帶令牌才能訪問。
改造Grpc.Client攜帶令牌訪問
//獲取token可以直接使用HttpClient來獲取,這裡使用IdentityModel來獲取token
var httpClient = new HttpClient();
var disco = await httpClient.GetDiscoveryDocumentAsync("http://localhost:5000");
if (!disco.IsError)
{
var token = await httpClient.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest()
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret"
});
var tokenValue = "Bearer " + token.AccessToken;
var metadata = new Metadata
{
{ "Authorization", tokenValue }
};
var callOptions = new CallOptions(metadata);
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var response = client.SayHello(new HelloRequest { Name = "World" }, callOptions);
Console.WriteLine(response.Message);
}
執行程式返回hello world表示成功。
傳統呼叫webapi把token放到Header頭的Authorization屬性裡面,grpc是放到Metadata裡面,呼叫方法的時候傳入CallOptions。使用上大同小異。
後記
目前gRPC各個語言的支援都已經很完善,因為跨語言,效能更高的特性非常適合做內網的通訊。筆者也將繼續對gRPC進行跟進,會嘗試將部分的內部服務改造成gRPC,關於gRPC的相關問題也可以留言大家一起討論。
原始碼地址:github