使用 IdentityServer4 客戶端憑證(ClientCredentials)訪問受保護資源

前言:

  1. 由於是保護客戶端,所以使用的授權類型爲客戶端憑證(ClientCredentials)。
  2. 這篇博文的前提是已經安裝了IdentityServer4的官方模板,安裝官方模板在命令行中執行dotnet new -i IdentityServer4.Templates即可,前提是你已經安裝了 .NET Core2.1+ 的版本。


一、創建項目

創建項目時用的命令:

$ mkdir Tutorial-Plus
$ cd Tutorial-Plus
$ mkdir src
$ cd src
$ dotnet new api -n Api
$ dotnet new is4inmem -n IdentityServer
$ dotnet new console -n ConsoleClient
$ cd ..
$ dotnet new sln -n Tutorial-Plus
$ dotnet sln add ./src/Api/Api.csproj
$ dotnet sln add ./src/IdentityServer/IdentityServer.csproj
$ dotnet sln add ./src/ConsoleClient/ConsoleClient.csproj

按順序運行命令整個解決方案初始化創建完畢。
解決方案中有 ApiConsoleClientIdentityServer 三個項目。

二、IdentityServer 項目

修改 IdentityServer 項目啓動端口爲 5000

1) 將 json config 修改爲 code config

在 IdentityServer 項目的 Startup.cs 文件的 ConfigureServices 方法中,
找到以下代碼:

    // in-memory, code config
    //builder.AddInMemoryIdentityResources(Config.GetIdentityResources());
    //builder.AddInMemoryApiResources(Config.GetApis());
    //builder.AddInMemoryClients(Config.GetClients());
    
    // in-memory, json config
    builder.AddInMemoryIdentityResources(Configuration.GetSection("IdentityResources"));
    builder.AddInMemoryApiResources(Configuration.GetSection("ApiResources"));
    builder.AddInMemoryClients(Configuration.GetSection("clients"));

將其修改爲

    // in-memory, code config
    builder.AddInMemoryIdentityResources(Config.GetIdentityResources());
    builder.AddInMemoryApiResources(Config.GetApis());
    builder.AddInMemoryClients(Config.GetClients());
    
    // in-memory, json config
    //builder.AddInMemoryIdentityResources(Configuration.GetSection("IdentityResources"));
    //builder.AddInMemoryApiResources(Configuration.GetSection("ApiResources"));
    //builder.AddInMemoryClients(Configuration.GetSection("clients"));

以上修改的內容爲將原來卸載配置文件中的配置,改爲代碼配置。

2) Config.cs 修改

在 Config.cs 文件中,有 GetIdentityResourcesGetApisGetClients 三個方法。
GetIdentityResources 方法是 被保護的 IdentityResource
GetApis 方法是 被保護的 ApiResource
GetClients 方法是用來配置客戶端

將 GetClients 方法修改爲:

    public static IEnumerable<Client> GetClients()
    {
        return new[]
        {
            // client credentials flow client
            new Client
            {
                ClientId = "client",
                ClientName = "Client Credentials Client",
    
                AllowedGrantTypes = GrantTypes.ClientCredentials,
                ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) },
    
                AllowedScopes = { "api1" }
            }
        };
    }

在上面代碼中,由於我們使用的是 ClientCredentials 授權方式。
Client Credentials - OAuth 2.0 中寫到:當應用程序請求訪問令牌訪問其自己的資源而不是代表用戶訪問時,將使用客戶端憑據授予。
也就是說,ClientCredentials 授權方式不代表任何用戶,不代表任何用戶也就是無法訪問IdentityResource資源,
所以當定義客戶端時,如果設置爲ClientCredentials 授權方法,那麼AllowedScopes屬性只能爲GetApis中定義的 ApiResource。

三、Api 項目

修改 Api 項目啓動端口爲 5001

1) 配置 Startup.cs

將 Api 項目的 Startup.cs 修改爲如下。

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvcCore().AddAuthorization().AddJsonFormatters();
            services.AddAuthentication("Bearer")
                .AddJwtBearer("Bearer", options =>
                {
                    options.Authority = "http://localhost:5000"; // IdentityServer的地址
                    options.RequireHttpsMetadata = false; // 不需要Https

                    options.Audience = "api1"; // 和資源名稱相對應
                });
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseAuthentication();
            app.UseMvc();
        }
    }

2) IdentityController.cs 文件

將 Controllers 文件夾中的 ValuesController.cs 改名爲 IdentityController.cs
並將其中代碼修改瞭如下:

    [Route("[controller]")]
    [ApiController]
    [Authorize]
    public class IdentityController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
        }
    }

在授權方法爲 ClientCredentials 時,上面代碼中的 User 指的是 客戶端應用。

四、ConsoleClient 項目

1) 修改ConsoleClient.csproj 文件

將 ConsoleClient 項目中的 ConsoleClient.csproj 文件修改爲如下:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="IdentityModel" Version="3.10.10" />
  </ItemGroup>
</Project>

新增的<LangVersion>latest</LangVersion>表示支持C# 7.1 特性。
新增的<PackageReference Include="IdentityModel" Version="3.10.10" /> 表示安裝 NuGit 包 IdentityModel。

2) 修改Program.cs 文件

修改 Program.cs 文件爲:

    static async Task Main(string[] args)
    {
        // discovery endpoint
        var client = new HttpClient();
        var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
    
        if (disco.IsError)
        {
            Console.WriteLine(disco.Error);
            return;
        }
    
        // request access token
        var tokenResponse = await client.RequestClientCredentialsTokenAsync(
            new ClientCredentialsTokenRequest
            {
                Address = disco.TokenEndpoint, // Token 端點
                ClientId = "client", 
                ClientSecret = "511536EF-F270-4058-80CA-1C89C192F69A",
                Scope = "api1"
            });
    
        if (tokenResponse.IsError)
        {
            Console.WriteLine(tokenResponse.Error);
            return;
        }
    
        // call Identity Resource API
        var apiClient = new HttpClient();
        apiClient.SetBearerToken(tokenResponse.AccessToken);
        var response = await apiClient.GetAsync("http://localhost:5001/identity");
        if (!response.IsSuccessStatusCode)
            Console.WriteLine(response.StatusCode);
        else
        {
            var content = await response.Content.ReadAsStringAsync();
            Console.WriteLine(JArray.Parse(content));
        }
    
        Console.ReadKey();
    }


參考文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章