1

应用程序中有四个客户端:

  1. angular.application -资源所有者
  2. identity_ms.client - webapi 应用程序(.net 核心 2.1)
    • IdentityServer4 与 AspNetIdentity
    • AccountController具有用于注册用户、重置密码等的共享操作。
    • 具有安全操作的用户控制器。UserControllerData动作有一个属性[Authorize(Policy = "user.data")]
  3. ms_1.client - webapi 应用程序(.net 核心 2.1)
  4. request.client -专门添加从 ms_1.client 向 identity_ms.client 的 UserController 发送请求以获取一些用户数据。

我正在使用 Postman 请求客户:

  1. http://localhost:identity_ms_port/connect/token获取access_token
  2. http://localhost:ms_1_port/api/secured/action从 ms_1 获取一些安全数据
  3. http://localhost:identity_ms_port/api/user/ data 从identity_ms获取一些安全的用户数据

一切正常。

此外,ms_1服务具有请求http://localhost:identity_ms_port/api/user/data使用的安全操作System.Net.Http.HttpClient

// identity_ms configuration
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(/*cors options*/);

    services
        .AddMvc()
        .AddApplicationPart(/*Assembly*/)
        .AddJsonOptions(/*SerializerSettings*/)
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.Configure<IISOptions>(iis =>
    {
        iis.AuthenticationDisplayName = "Windows";
        iis.AutomaticAuthentication = false;
    });

    var clients = new List<Client>
    {
        new Client
    {
            ClientId = "angular.application",
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            AllowedScopes = { "user.data.scope", "ms_1.scope", "identity_ms.scope" },
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword
    },
        new Client
        {
            ClientId = "ms_1.client",
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            AllowedScopes = { "user.data.scope", "ms_1.scope" },
            AllowedGrantTypes = GrantTypes.ClientCredentials
        },
        new Client
        {
            ClientId = "identity_ms.client",
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            AllowedScopes =
            {
                "user.data.scope",
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile
            },
            AllowedGrantTypes = GrantTypes.Implicit
        },
        new Client
        {
            ClientId = "request.client",
            AllowedScopes = { "user.data.scope" },
            AllowedGrantTypes = GrantTypes.ClientCredentials,
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            }
        }
    };
    var apiResources = new List<ApiResource>
    {
        new ApiResource("ms_1.scope", "MS1 microservice scope"),
        new ApiResource("identity_ms.scope", "Identity microservice scope"),
        new ApiResource("user.data.scope", "Requests between microservices scope")
    };

    var identityResources = new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile()
    };

    services
        .AddAuthorization(options => options.AddPolicy("user.data", policy => policy.RequireScope("user.data.scope")))
        .AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryIdentityResources(identityResources)
        .AddInMemoryApiResources(apiResources)
        .AddInMemoryClients(clients);

    services
        .AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {
            options.Audience = "identity_ms.scope";
            options.RequireHttpsMetadata = false;
            options.Authority = "http://localhost:identity_ms_port";
        });

    services.AddSwaggerGen(/*swagger options*/);
}

public void Configure(IApplicationBuilder app)
{
    app.UseMiddleware<CustomMiddleware>();
    app.UseIdentityServer();
    app.UseAuthentication();
    app.UseCors("Policy");
    app.UseHttpsRedirection();
    app.UseMvc(/*routes*/);
    app.UseSwagger();
}

// ms_1.client configuration
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(/*cors options*/);

    services
        .AddMvc()
        .AddJsonOptions(/*SerializerSettings*/)
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services
        .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
        {
            options.Audience = "ms_1.scope";
            options.RequireHttpsMetadata = false;
            options.Authority = "http://localhost:identity_ms_port";
        });
}

public void Configure(IApplicationBuilder app)
{
    app.UseMiddleware<CustomMiddleware>();
    app.UseAuthentication();
    app.UseCors("Policy");
    app.UseStaticFiles();
    app.UseHttpsRedirection();
    app.UseMvc(/*routes*/);
    app.UseSwagger();
}

// ms_1.client action using HttpClient
[HttpPost]
public async Task<IActionResult> Post(ViewModel model)
{
    //...
    using (var client = new TokenClient("http://localhost:identity_ms_port/connect/token", "ms_1.client", "secret"))
    {
        var response = await client.RequestClientCredentialsAsync("user.data.scope");

        if (response.IsError)
        {
            throw new Exception($"{response.Error}{(string.IsNullOrEmpty(response.ErrorDescription) ? string.Empty : $": {response.ErrorDescription}")}", response.Exception);
        }

        if (string.IsNullOrWhiteSpace(response.AccessToken))
        {
            throw new Exception("Access token is empty");
        }

        var udClient = new HttpClient();

        udClient.SetBearerToken(response.AccessToken);
        udClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        var result = await udClient.GetAsync("http://localhost:identity_ms_port/api/user/data");
    }
    //...
}

我尝试了以下方法:

  1. 从请求中检索access_token到ms_1 Authorization 标头并使用它来访问user/data
  2. 获取新的access_token以使用它访问用户/数据。查看public async Task<IActionResult> Post(ViewModel model)代码块中的代码。

在这两种情况下,我都获得了正确的令牌,可用于从 Postman 请求安全/操作用户/数据操作,但 HttpClient 得到未经授权的响应 (401)。

响应标头屏幕截图

我究竟做错了什么?

4

1 回答 1

0

在您的客户端代码中,HttpClient您没有请求 API 的任何范围,因此 Identity Server 4 发出的令牌将不包含作为受众之一的 API,然后您将从 API 获得 401。

更改您的令牌请求以请求 API 范围。

var response = await client.RequestClientCredentialsAsync("user.data.scope ms_1.scope");
于 2019-02-13T08:56:46.080 回答