IdentityServer4(二):使用Client Credentials模式控制API的访问

本文基于官方文章翻译和修改

客户端凭证模式(Client Credentials)模式

客户端凭证模式模式是IdentityServer4中控制API访问的基本模式,我们通常通过定义一个API和访问客户端,客户端通过客户端ID请求Identity Server以获取访问的token

本篇将通过创建一个用户认证授权中心来保护API

编写Identity server

Install-Package IdentityServer4 -Version 3.1.0

定义API Resource和客户端

public static class Config
{
    // Defining an API Resource
    public static IEnumerable<ApiResource> Apis =>
        new List<ApiResource>{
            new ApiResource("CodeSnippets.WebApi","CodeSnippets API")
        };

    // Defining Client
    public static IEnumerable<Client> Clients =>
        new List<Client> {
            new Client
            {
                ClientId="client",

                // no interactive user, use the clientid/secret for authentication
                AllowedGrantTypes=GrantTypes.ClientCredentials,

                // secret for authentication
                ClientSecrets={
                    new Secret("secret".Sha256())
                },

                // scopes that client has access to
                AllowedScopes={ "CodeSnippets.WebApi" }
            }
        };
}

配置IdentityServer

public void ConfigureServices(IServiceCollection services)
{
    var builder = services.AddIdentityServer()
        .AddInMemoryApiResources(Config.Apis)
        .AddInMemoryClients(Config.Clients);
    // builder.AddDeveloperSigningCredential();
}

配置完成后运行应用,访问:http://localhost:5000/.well-known/openid-configuration将看到所谓的发现文档(发现文档是身份服务器中的标准端点,我们的客户端和API将使用发现文档来下载必要的配置数据)。
在这里插入图片描述
首次启动时,IdentityServer将为我们创建一个开发人员签名密钥,该文件名为tempkey.rsa。我们无需将该文件签入源代码管理中,如果不存在该文件将被重新创建。

创建API

  • 新建一个WebApi:
[Route("identity")]
[ApiController]
[Authorize]
public class IdentityController : ControllerBase
{
    public IActionResult Get()
    {
        return new JsonResult(User.Claims.Select(c => new { c.Type, char.MinValue }));
    }
}

配置launchSettings.json使其在端口5001运行

  • 配置API
public void ConfigureServices(IServiceCollection services)
{
    // 将身份验证服务添加到DI并配置Bearer为默认方案。
    services.AddAuthentication("Bearer")
        .AddJwtBearer("Bearer", option =>
        {
            option.Authority = "http://localhost:5000";
            option.RequireHttpsMetadata = false;
            option.Audience = "CodeSnippets.WebApi";
        });
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseRouting();

    app.UseAuthentication();    // 将身份验证中间件添加到管道中,以便对主机的每次调用都将自动执行身份验证。
    app.UseAuthorization();    // 添加了授权中间件,以确保匿名客户端无法访问我们的API端点。

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute().RequireAuthorization();
        //endpoints.MapHealthChecks("")
    });
}

此时运行API,访问http://localhost:5001/identity返回401 Unauthorized

创建客户端

客户端请求访问令牌,然后使用该令牌访问API

IdentityServer上的令牌端点实现了OAuth 2.0协议,我们可以使用原始HTTP来访问它。但是,我们有一个名为IdentityModel的客户端库,该客户端库将协议交互封装在易于使用的API中:

Install-Package IdentityModel -Version 4.1.1

  • 步骤一 获取被发现文档
    IdentityModel包括一个与发现端点一起使用的客户端库。这样,我们只需要知道IdentityServer的基地址-实际的端点地址就可以从元数据中读取。

  • 步骤二
    使用发现文档中的信息向IdentityServer请求令牌以访问API

  • 步骤三
    调用API

static async Task Main(string[] args)
{
    // 1.discover endpoints from metadata
    var client = new HttpClient();
    var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
    if (disco.IsError)
    {
        Console.WriteLine(disco.Error);
        return;
    }

    // 2.request token
    var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
    {
        Address = disco.TokenEndpoint,
        ClientId = "client",
        ClientSecret = "secret",
        Scope = "CodeSnippets.WebApi"
    });

    if (tokenResponse.IsError)
    {
        Console.WriteLine(tokenResponse.Error);
        return;
    }
    Console.WriteLine(tokenResponse.Json);
    Console.WriteLine("*".PadLeft(50, '*'));
    // 3.call 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(content);
        //Console.WriteLine(Newtonsoft.Json.Linq.JArray.Parse(content));
    }
}

以上代码执行结果:
在这里插入图片描述

发布了110 篇原创文章 · 获赞 36 · 访问量 31万+

猜你喜欢

转载自blog.csdn.net/zhaobw831/article/details/103726267
今日推荐