1. 程式人生 > >.Net Core使用Ocelot閘道器(二) -鑑權認證

.Net Core使用Ocelot閘道器(二) -鑑權認證

前言

上一章已經簡單的介紹了ocelot的使用了,但是閘道器暴露的介面如果什麼人都能訪問的話安全性就太低啦。所以我們需要去鑑權和認證。這裡我們使用identityServer4給我們的閘道器來鑑權認證。

建立Identity服務

我們建立一個identity的服務來用於令牌的發放和鑑權。下圖是我的專案結構。

Api_Gatewat埠:5000
Api_A埠:5001
Api_B埠:5002
IdentityServer埠:5003

通過nuget新增IdentityServer4的包,也可以通過程式包管理控制檯執行以下命令Install-Package IdentityServer4

新增一個Congif檔案。

using System.Collections.Generic;
using IdentityModel;
using IdentityServer4;
using IdentityServer4.Models;

namespace IdentityServer
{
    public static class Config
    {
        public static IEnumerable<IdentityResource> GetIdentityResourceResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(), //必須要新增,否則報無效的scope錯誤
            };
        }
        // scopes define the API resources in your system
        public static IEnumerable<ApiResource> GetApiResources()
        {
            //可訪問的API資源(資源名,資源描述)
            return new List<ApiResource>
            {
                new ApiResource("Api_A", "Api_A"),
                new ApiResource("Api_B", "Api_B")
            };
        }

        public static IEnumerable<Client> GetClients()
        { 
            return new List<Client>
            {
                new Client
                {
                    ClientId = "client_a", //訪問客戶端Id,必須唯一
                    //使用客戶端授權模式,客戶端只需要clientid和secrets就可以訪問對應的api資源。
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedScopes = { "Api_A",IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.Profile }
                },
                new  Client
                {
                    ClientId = "client_b",
                    ClientSecrets = new [] { new Secret("secret".Sha256()) },
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    AllowedScopes = { "Api_B",IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.Profile }
                }
            };
        }
    }
}

新增兩個API資源,並且新增兩個客戶端分別去訪問不同資源。

Startup 中的 ConfigureServices 中配置IdentityServer服務。

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients());
}

Configure 中把IdentityServer放入http管道中。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseIdentityServer();
}

為ocelot整合Identity

通過nuget新增IdentityServer4.AccessTokenValidation的包,也可以通過程式包管理控制檯執行以下命令 Install-Package IdentityServer4.AccessTokenValidation

IdentityServer4.AccessTokenValidation - 用於驗證IdentityServer4中的JWT和引用令牌

StartupConfigureServices 中分別註冊兩個認證方案 Configure 中配置IdentityServer服務。

public void ConfigureServices(IServiceCollection services)
{

    services.AddAuthentication()
            .AddJwtBearer("Api_A", i =>
            {
                i.Audience = "Api_A";
                i.Authority = "http://localhost:5003";
                i.RequireHttpsMetadata = false;
            }).AddJwtBearer("Api_B", y =>
            {
                y.Audience = "Api_B";
                y.Authority = "http://localhost:5003";
                y.RequireHttpsMetadata = false;
            });
    services.AddOcelot(new ConfigurationBuilder()
            .AddJsonFile("configuration.json")
            .Build());
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseOcelot();
    app.UseAuthorization();
}

並修改ocelot配置檔案,在Routes中新增授權資訊

{
  "ReRoutes": [
    {
      "UpstreamPathTemplate": "/Api_A/{controller}/{action}",
      "DownstreamPathTemplate": "/api/{controller}/{action}",
      "UpstreamHttpMethod": [ "GET", "POST", "DELETE", "PUT" ],
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5001
        }

      ],
      "RateLimitOptions": {
        "ClientWhitelist": [ "127.0.0.1" ],
        "EnableRateLimiting": true,
        "Period": "1m",
        "PeriodTimespan": 30,
        "Limit": 5
      },
      "FileCacheOptions": {
        "TtlSeconds": 5,
        "Region": "time"
      },
      "UpstreamHeaderTransform": {
        "demo": "a,b"
      },
      "DownstreamHeaderTransform": {
        "demo": "xxxxxxx",
        "Location": "{DownstreamBaseUrl},{BaseUrl}"
      },
      //授權資訊
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "Api_A",
        "AllowedScopes": []
      }
    },
    {
      "UpstreamPathTemplate": "/Api_B/{controller}/{action}",
      "DownstreamPathTemplate": "/api/{controller}/{action}",
      "UpstreamHttpMethod": [ "GET", "POST", "DELETE", "PUT" ],
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",  
          "Port": 5002
        }

      ],
      //授權資訊
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "Api_B",
        "AllowedScopes": []
      }
    }
  ],
  "QoSOptions": {
    "ExceptionsAllowedBeforeBreaking": 3,
    "DurationOfBreak": 20,
    "TimeoutValue": 5000
  },
  "GlobalConfiguration": {
    "RateLimitOptions": {
      "DisableRateLimitHeaders": false,
      "QuotaExceededMessage": "介面限流!",
      "HttpStatusCode": 200,
      "ClientIdHeader": "ClientId"
    }
  }
}

Ocelot會去檢查ReRoutes是否配置了AuthenticationOptions節點。如果有會根據配置的認證方案進行身份認證。如果沒有則不進行身份認證。
AuthenticationProviderKey 是剛才註冊的認證方案。
AllowedScopes 是 AllowedScopes中配置的授權訪問範圍。

演示效果

我們為api_a和api_b分別註冊了認證方案。如果我們不申請token是會401沒有許可權訪問。

我們通過identityServer申請一個的token,並用它訪問api_a和api_b。



可以看到我們申請的token是可以訪問api_a的,但是不能訪問api_b,因為client_a這個客戶端只有訪問api_a的權利。如果想訪問api_b使用client_b申請token就可以啦。

總結

簡單為Ocelot集成了IdentityServer,希望對大家有參考價值。如果文中有錯誤請聯絡我更改