使用Ocelot、IdentityServer4、Spring Cloud Eureka搭建微服务网关:Step by Step(二)
本文继续介绍使用Ocelot、IdentityServer4以及Spring Cloud Eureka搭建微服务网关的步骤。相关文章:
Step 4:引入Ocelot API网关
新建一个ASP.NET Core API项目,添加对以下NuGet包的引用:
- Ocelot
- IdentityServer4
- IdentityServer4.AccessTokenValidation
- Serilog(我使用了Serilog来输出格式化的日志,如果不使用Serilog,则不需要引用)
首先,修改Startup.cs文件,用来启用Ocelot,并指定身份认证机构为之前我们建好的IdentityServer服务:
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddAuthentication() .AddIdentityServerAuthentication("AuthProviderKey", options => { options.Authority = "http://localhost:5000"; options.RequireHttpsMetadata = false; options.ApiName = "country_code_api"; options.SupportedTokens = SupportedTokens.Both; }); services.AddOcelot(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); app.UseOcelot().Wait(); }
注意代码中的高亮部分,分别指定了身份认证机构的URL地址、所需认证的API名称,以及对Ocelot网关的启用。接下来,需要配置Ocelot网关,使其在充当网关角色的同时,完成身份验证的过程。下面的JSON文件(ocelot.config.json)对API网关的Upstream、Downstream以及身份认证相关信息进行了设置。注意,在每个API的AuthenticationOptions设置中,AuthenticationProviderKey应该使用上面Startup.cs中指定的那个Key(也就是AddIdentityServerAuthentication方法的第一个参数)。
{ "ReRoutes": [ { "DownstreamPathTemplate": "/api/countries", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5001 } ], "UpstreamPathTemplate": "/countries", "UpstreamHttpMethod": [ "Get" ], "AuthenticationOptions": { "AuthenticationProviderKey": "AuthProviderKey", "AllowedScopes": [] } }, { "DownstreamPathTemplate": "/api/countries/{code}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5001 } ], "UpstreamPathTemplate": "/countries/{code}", "UpstreamHttpMethod": [ "Get" ], "AuthenticationOptions": { "AuthenticationProviderKey": "AuthProviderKey", "AllowedScopes": [] } } ], "GlobalConfiguration": { "RequestIdKey": "OcRequestId", "AdministrationPath": "/administration" } }
然后,修改Program.cs文件,将ocelot.config.json文件加入项目的配置系统中,以便Ocelot能够读取并使用里面的配置。当然,如果不打算使用Serilog,可以忽略UseSerilog的相关设置。
public static IWebHostBuilder CreateWebHostBuilder(string[] args) { return WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .ConfigureAppConfiguration((context, builder) => { builder.AddJsonFile("ocelot.config.json"); }) .UseSerilog((context, logger) => { logger.ReadFrom.Configuration(context.Configuration); }); }
最后,修改launchSettings.json,使得Ocelot API网关侦听5002端口。于是,有关Ocelot网关的加入和基本配置就完成了。如果需要了解Ocelot使用的更多信息,可以参考我之前的文章《ASP.NET Core中Ocelot的使用:API网关的应用》。
Step 5:通过Ocelot API网关访问API
在本系列文章第一篇中所演示的控制台应用程序中,做一些小的改动,即可实现通过Ocelot API网关访问需身份认证的API。我们只需要将API的访问地址从原来的直接访问的URL,改为Ocelot所配置的URL即可:
static async Task Main(string[] args) { using (var client = new HttpClient()) { // IdentityModel为HttpClient提供了基于认证模型的API扩展 // 获取Access Token var discoResponse = await client.GetDiscoveryDocumentAsync("http://localhost:5000"); if (discoResponse.IsError) { Console.WriteLine(discoResponse.Error); return; } var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = discoResponse.TokenEndpoint, ClientId = "country_api_client", ClientSecret = "abcdef", Scope = "country_code_api" }); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } // 输出获取Access Token的API返回结果 Console.WriteLine(tokenResponse.Json); // 在HttpClient上设置Access Token client.SetBearerToken(tokenResponse.AccessToken); // 调用API并返回结果 var response = await client.GetAsync("http://localhost:5002/countries/cn"); Console.WriteLine(response.IsSuccessStatusCode ? $"{response.StatusCode} {await response.Content.ReadAsStringAsync()}" : response.StatusCode.ToString()); } }
现在,启动身份认证服务IdentityServer、Countries API服务、Ocelot API网关,然后运行控制台应用程序,可以看到,API可以正常返回并输出正确结果:
接下来做个试验,就是注释掉Countries API上的Authorize特性,让Countries API在没有认证的情况下也能被访问,然后,再次启动IdentityServer、Countries API服务以及Ocelot网关服务,再直接使用cURL进行调用,此时我们没有提供任何认证的令牌信息,可以看到,服务端拒绝了我们的请求,并返回了HTTP 401 Unauthorized:
由此可见,即便是API没有实现认证机制,Ocelot API网关也能代理完成认证的功能,这也使得一个微服务的项目能够通过API网关来统一完成身份认证,背后的API无需考虑身份认证的工作(授权是另一个概念,今后会讨论)。
小结
本文简要介绍了如何接入Ocelot API网关,结合IdentityServer4完成微服务API的身份认证。后续的文章会讨论另一个话题:授权。以下是本文所描述场景的UML时序图,供参考:
[plantuml]
@startuml IdsSample_Chapter2_Sequence
actor ClientApp
ClientApp -> IdentityServer: getAccessToken
IdentityServer --> ClientApp: accessToken
ClientApp -> OcelotAPIGateway: getCountriesByCode(countryCode, accessToken)
OcelotAPIGateway -> IdentityServer: authenticate(accessToken)
IdentityServer --> OcelotAPIGateway: success
OcelotAPIGateway -> CountriesAPI: getCountriesByCode(countryCode)
CountriesAPI -> CountriesAPI: findCountriesByCode(countryCode)
CountriesAPI --> OcelotAPIGateway: countries
OcelotAPIGateway --> ClientApp: countries
@enduml
[/plantuml]