使用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可以正常返回并输出正确结果:

image

接下来做个试验,就是注释掉Countries API上的Authorize特性,让Countries API在没有认证的情况下也能被访问,然后,再次启动IdentityServer、Countries API服务以及Ocelot网关服务,再直接使用cURL进行调用,此时我们没有提供任何认证的令牌信息,可以看到,服务端拒绝了我们的请求,并返回了HTTP 401 Unauthorized:

image

由此可见,即便是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]

posted @ 2019-03-02 20:35  dax.net  阅读(518)  评论(0编辑  收藏  举报