使用 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();
    }


参考文档

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