如何使用带有Bearer令牌的HttpClient访问[授权]控制器动作?得到401“观众无效”

时间:2019-02-12 17:40:41

标签: identityserver4 dotnet-httpclient asp.net-core-2.1 bearer-token audience

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

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

我正在使用邮递员请求客户:

  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
  4. 获取一些安全的用户数据

一切正常。

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

// 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. 要从对 ms_1 授权标头的请求中检索 access_token ,并使用它来访问用户/数据
  2. 要获取新的 access_token 来访问用户/数据。 请参见代码块中的public async Task<IActionResult> Post(ViewModel model)代码。

在两种情况下,我都有正确的令牌,可用于请求Postman的 secured / action user / data 操作,但是HttpClient正在获取未经授权的回复(401)。

Response headers screenshot

我在做什么错了?

1 个答案:

答案 0 :(得分:0)

在带有HttpClient的客户端代码中,您没有请求该API的任何作用域,因此,由Identity Server 4发行的令牌将不包含该API作为受众之一,随后您将获得401的标识。 API。

更改令牌请求,同时询问API范围。

var response = await client.RequestClientCredentialsAsync("user.data.scope ms_1.scope");