身份服务器 4

Identity Server 4

初学者级别的查询警报。 IdentityServer4 Tutorial 看完教程后我推断是-

  1. 我创建了一个授权服务器,其工作是通过适当的身份验证为客户端颁发令牌。
  2. 首先是我的授权服务器 运行,包括 API 和客户端的信息和定义。
  3. API 有一个验证传入令牌的身份验证中间件,以确保它是否来自受信任的来源及其范围。
  4. 客户端从授权服务器请求令牌,然后将请求发送到 API 并收到令牌。

对于所有这些,我必须先 运行 授权服务器,然后是 API,然后是客户端。我的要求是我不需要单独启动和停止服务器 运行 来处理身份验证。我有一个 API,我也需要它兼作授权服务器。这可能吗? API 是否有可能生成令牌、验证它们然后处理请求,同时一直使用 IdentityServer4。

2020 年 1 月更新:对于在与 ASP.NET 核心 API 控制器相同的项目中使用 IdentityServer4 的 ASP.NET 核心 3.1 示例,你可以看看我的IdentityServer4 with MVC Controllers and AppInsights sample repo。它的目标是测试 AppInsights,但它确实演示了一个 SPA 存根,它调用两个 OpenID 端点(⚠ 在 non-recommended wa 中,使用客户端凭据)和控制器端点。


虽然通常 Auth Server 将与 Resource Server 分开,但这并不需要是这种情况。您可以将所有这些添加到一个应用程序中。这是一个例子。

  1. 创建一个新的 ASP.NET Core(我用的是 2.0)Web API 应用程序。
  2. Install-Package IdentityServer4 -Version 2.0.0-rc1(在编写 rc1 时是支持 .NET Core 2.x 的版本)
  3. Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
  4. 通过模板在 ValuesController 上设置 [Authorize]
  5. 将此代码添加到 class Startup 上面 app.UseMvc() 中的 Configure(...):

    // calls app.UseAuthentication() for us
    // See: http://docs.identityserver.io/en/release/quickstarts/6_aspnet_identity.html
    app.UseIdentityServer();
    
  6. 将此代码添加到 class Startup 中的 ConfigureServices(...):

    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryApiResources(new[]
        {
            new ApiResource
            {
                Name = "MyApi",
                ApiSecrets = { new Secret("supersecret".Sha256()) },
                Scopes = { new Scope("myapi") },
            }
        })
        .AddInMemoryClients(new[]
        {
            new Client
            {
                ClientId = "api",
                ClientSecrets = { new Secret("supersecret".Sha256()) },
                AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                AllowedScopes = { "myapi" },
            }
        })
        .AddTestUsers(new List<TestUser>
        {
            new TestUser
            {
                SubjectId = "some-unique-id-12345678980",
                Username = "john",
                Password = "123456"
            }
        });
    
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(opts =>
        {
            opts.Authority = "http://localhost:51689";
            opts.Audience = "MyApi";
            opts.RequireHttpsMetadata = !env.IsDevelopment();
        });
    

如果您现在 F5 应用程序,它将显示一个空白页面,因为“401 未经授权”响应。您现在还可以检查此端点:http://localhost:51689/.well-known/openid-configuration(当然还有您的开发端口)。

您现在也可以这样做:

curl -X POST \
  http://localhost:51689/connect/token \
  -H 'authorization: Basic YXBpY2xpZW50aWQ6c3VwZXJzZWNyZXQ=' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d 'username=john&password=123456&grant_type=password'

请注意 authorization header 包含表示字符串 "apiclientid:supersecret" 的 base64 编码字符串。这应该会给你这样的结果:

{
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjczODhkMjY0MDg4Y2NjOGRiZTcwODIzZGIxYzY3ZWNkIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MDUwODE3OTAsImV4cCI6MTUwNTA4NTM5MCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MTY4OSIsImF1ZCI6WyJodHRwOi8vbG9jYWxob3N0OjUxNjg5L3Jlc291cmNlcyIsIk15QXBpIl0sImNsaWVudF9pZCI6ImFwaWNsaWVudGlkIiwic3ViIjoic29tZS11bmlxdWUtaWQtMTIzNDU2Nzg5ODAiLCJhdXRoX3RpbWUiOjE1MDUwODE3OTAsImlkcCI6ImxvY2FsIiwic2NvcGUiOlsibXlhcGkiXSwiYW1yIjpbInB3ZCJdfQ.sxWodlJKDJgjoOj-8njZ8kONOqiKgj3E5YlKXGX5cz-WqUK7RHKJacNX09D00Y8YtmZpkc5OrY0xzOx7UuSAtDku4oOX_1o38XEGJPBSJHdjqgVGSOU-hwDkzin8HSRJ0Kna1vM3ZzTh80cFTVhP8h903GAPRrAyV8PtRXnwV0CPel8NdvML6dV-mfDpGi0l7crp-TPnH4nIG0olpRYUPV5EsgCVMG9vswnOnKz3RPOGaU8yJy7_9mbQW5GHKfN0J6swiSt5rY3NKs_t1P9-tnCDKBOAafaXjLEO3Kx4fP4xTgwK92uKcEDDnRZo_-T0CkBxnSQm0oz1sUyrW8_3Pg",
    "expires_in": 3600,
    "token_type": "Bearer"
}

除了切换到其他身份验证流程的选项外,您还可以添加这样的控制器方法:

[Route("api/token")]
public class TokenController
{
    [HttpPost("request")]
    public async Task<JObject> Request(string username, string password)
    {
        var tokenClient = new TokenClient("http://localhost:51689/connect/token", "apiclientid", "supersecret");
        var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(username, password);

        if (tokenResponse.IsError) { /* Log failed login attempt! */ } 

        return tokenResponse.Json;
    }
}

然后这样称呼它:

curl -X POST \
  http://localhost:51689/api/token/request \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d 'username=john&password=123456'

这应该给出与上面类似的响应。

您现在可以像这样提供 access_token 插入 header Authorization: Bearer access_token_should_go_here

curl -X GET \
  http://localhost:51689/api/values \
  -H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjczODhkMjY0MDg4Y2NjOGRiZTcwODIzZGIxYzY3ZWNkIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MDUwODIyODQsImV4cCI6MTUwNTA4NTg4NCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MTY4OSIsImF1ZCI6WyJodHRwOi8vbG9jYWxob3N0OjUxNjg5L3Jlc291cmNlcyIsIk15QXBpIl0sImNsaWVudF9pZCI6ImFwaWNsaWVudGlkIiwic3ViIjoic29tZS11bmlxdWUtaWQtMTIzNDU2Nzg5ODAiLCJhdXRoX3RpbWUiOjE1MDUwODIyODQsImlkcCI6ImxvY2FsIiwic2NvcGUiOlsibXlhcGkiXSwiYW1yIjpbInB3ZCJdfQ.hQ60zzEbZOSVpP54yGAnnzfVEks18YXn3gU2wfFgNB33UxQabk1l3xkaeUPTpuFdmFTm4TbVatPaziGqaxjzYgfdVoAwQ3rYJMuYzOh0kUowKxXTkquAlD13ScpvxrGeCXGxFTRHrxX2h-1hHGQ9j2y2f3-ESynzrCdxp5HEH1271BSYfQ7pZIzvyxxpbmOzzKDzdYfcJV6ocnOU4jXBhw6iOzqpR03zxxtjIjGbJd2QwWklBGqZlO_thdZZFi-t7zu5eC4wqRCYGGZYWOUC17_Btc_Irg2SsvLCUDzsaBw7AVgLpZ7YjF-RsVqIi6oxNQ2K0zllzUy8VbupbWKr5Q' \
  -H 'cache-control: no-cache' \

现在您应该通过 [Authorize] 属性。耶!

您现在有一个 Web 应用程序,它同时充当 Auth Server 和 Resource Server。


有趣的事实:在上面的示例中,AddJwtBearer 选项将应用程序自己的 url 指定为 Authority,使应用程序从自身请求 public 键用于验证令牌。您也可以使用代码直接将此密钥提供给身份验证中间件。