本快速入门介绍了使用IdentityServer保护API的最基本方案。

在这种情况下,我们将定义一个API和一个想要访问它的客户端。客户端将在IdentityServer请求访问令牌并使用它来获取对API的访问权限。

定义

范围定义了您要保护的系统中的资源,例如API。

由于我们在本演练中使用内存配置 - 您只需创建一个类型的对象ApiResource并设置适当的属性即可。

将文件(例如Config.cs)添加到项目中并添加以下代码:

public static IEnumerable<ApiResource> GetApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource("api1", "My API")
    };
}

定义客户端

下一步是定义可以访问此API的客户端。

对于此方案,客户端将不具有交互式用户,并将使用IdentityServer的所谓客户端密钥进行身份验证。将以下代码添加到Config.cs文件中:

public static IEnumerable<Client> GetClients()
{
    return 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 = { "api1" }
        }
    };
}

配置IdentityServer 

要将IdentityServer配置为使用范围和客户端定义,您需要向ConfigureServices方法添加代码您可以使用方便的扩展方法 - 在封面下,这些将相关的存储和数据添加到DI系统中:

public void ConfigureServices(IServiceCollection services)
{
    // configure identity server with in-memory stores, keys, clients and resources
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients());
}

就是这样 - 如果您运行服务器并浏览浏览器 http://localhost:5000/.well-known/openid-configuration,您应该会看到所谓的发现文档。客户端和API将使用它来下载必要的配置数据。

../_images/1_discovery.png

添加

接下来,为您的解决方案添加API。

您可以使用ASP.NET Core Web API模板。同样,我们建议您控制端口并使用与以前配置Kestrel和启动配置文件相同的技术。本演练假定您已将API配置为运行http://localhost:5001

控制器

向API项目添加新控制器:

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

稍后将使用此控制器来测试授权要求,以及通过API的眼睛可视化声明身份。

组态

最后一步是将身份验证服务添加到DI和身份验证中间件到管道。这些将:

  • 验证传入令牌以确保它来自受信任的颁发者
  • 验证令牌是否有效用于此api(aka范围)

IdentityServer4.AccessTokenValidation NuGet包添加到项目中。

../_images/1_nuget_accesstokenvalidation.png

Startup更新为如下所示:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvcCore()
            .AddAuthorization()
            .AddJsonFormatters();

        services.AddAuthentication("Bearer")
            .AddIdentityServerAuthentication(options =>
            {
                options.Authority = "http://localhost:5000";
                options.RequireHttpsMetadata = false;

                options.ApiName = "api1";
            });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseAuthentication();

        app.UseMvc();
    }
}

AddAuthentication将身份验证服务添加到DI并配置"Bearer"为默认方案。AddIdentityServerAuthentication将IdentityServer访问令牌验证处理程序添加到DI中以供身份验证服务使用。 UseAuthentication将身份验证中间件添加到管道中,以便在每次调用主机时自动执行身份验证。

如果您使用浏览器导航到控制器(http://localhost:5001/identity),您应该获得401状态代码作为回报。这意味着您的API需要凭据。

就是这样,API现在受到IdentityServer的保护。

创建客户端

最后一步是编写请求访问令牌的客户端,然后使用此令牌访问API。为此,请向您的解决方案添加一个控制台项目(请参阅此处的完整代码)。

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

IdentityModel NuGet包添加到您的应用程序。

../_images/1_nuget_identitymodel.png

IdentityModel包括用于发现端点的客户端库。这样您只需要知道IdentityServer的基地址 - 可以从元数据中读取实际的端点地址:

// discover endpoints from metadata
var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
if (disco.IsError)
{
    Console.WriteLine(disco.Error);
    return;
}

接下来,您可以使用TokenClient该类来请求令牌。要创建实例,您需要传递令牌端点地址,客户端ID和密码。

接下来,您可以使用该RequestClientCredentialsAsync方法为您的API请求令牌:

// request token
var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

if (tokenResponse.IsError)
{
    Console.WriteLine(tokenResponse.Error);
    return;
}

Console.WriteLine(tokenResponse.Json);

注意

将访问令牌从控制台复制并粘贴到jwt.io以检查原始令牌。

最后一步是调用API。

要将访问令牌发送到API,通常使用HTTP Authorization标头。这是使用SetBearerToken扩展方法完成的

// call api
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);

var response = await client.GetAsync("http://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
    Console.WriteLine(response.StatusCode);
}
else
{
    var content = await response.Content.ReadAsStringAsync();
    Console.WriteLine(JArray.Parse(content));
}

输出应如下所示:

../_images/1_client_screenshot.png

注意

默认情况下,访问令牌将包含有关范围,生命周期(nbf和exp),客户端ID(client_id)和颁发者名称(iss)的声明。

进一步的实验

本演练重点关注目前的成功之路

  • 客户端能够请求令牌
  • 客户端可以使用令牌来访问API

您现在可以尝试激发错误以了解系统的行为,例如

  • 尝试在未运行时连接到IdentityServer(不可用)
  • 尝试使用无效的客户端ID或机密来请求令牌
  • 尝试在令牌请求期间请求无效范围
  • 尝试在API未运行时调用API(不可用)
  • 不要将令牌发送到API
  • 将API配置为需要与令牌中的范围不同的范围