IdentityServer4:Quickstart始终返回未经授权的API端点,消息为;观众';https://localhost:5001/resources' “无效”;

IdentityServer4:Quickstart始终返回未经授权的API端点,消息为;观众';https://localhost:5001/resources' “无效”;,identityserver4,Identityserver4,按照逐字逐句的说明,我无法执行客户端。始终返回未经授权的401 生成的令牌是: { "alg": "RS256", "kid": "57EDAEBEC68F3CAACE869E3FA226C0FF", "typ": "at+jwt" }.{ "nbf": 1593466354, "exp": 1593469954,

按照逐字逐句的说明,我无法执行客户端。始终返回未经授权的401

生成的令牌是:

{
  "alg": "RS256",
  "kid": "57EDAEBEC68F3CAACE869E3FA226C0FF",
  "typ": "at+jwt"
}.{
  "nbf": 1593466354,
  "exp": 1593469954,
  "iss": "https://localhost:5001",
  "aud": "https://localhost:5001/resources",
  "client_id": "client",
  "jti": "C76BC9CB471ED81832A56B78059421FB",
  "iat": 1593466354,
  "scope": [
    "api1"
  ]
}.[Signature]
但我看不出有什么办法能吸引观众

我的Api Startup.cs:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace api
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddAuthentication("Bearer")
                .AddJwtBearer("Bearer", options =>
                {
                    // Use our IS4 implementation as the authentication source.
                    options.Authority = "https://localhost:5001";
                    options.RequireHttpsMetadata = false;

                    options.Audience = "api1";
                });

            services.AddAuthorization(options =>
            {
                options.AddPolicy("ApiScopePolicy", policy =>
                {
                    policy.RequireAuthenticatedUser();
                    policy.RequireClaim("scope", "api1");
                });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();
            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
MyEndpointController

using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Api.Controllers
{
    [Route("MyEndpoint")]
    [Authorize]
    public class MyEndpointController : ControllerBase
    {
        [HttpGet]
        [Route("Get")]
        public IActionResult Get()
        {
            return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
        }
    }
}
我的身份服务器实现 Startup.cs

public class Startup
{
    public IWebHostEnvironment Environment { get; }

    public Startup(IWebHostEnvironment environment)
    {
        Environment = environment;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        // uncomment, if you want to add an MVC-based UI
        services.AddControllersWithViews();

        var builder = services.AddIdentityServer(options =>
        {
            // see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
            options.EmitStaticAudienceClaim = true;
        })
            .AddInMemoryIdentityResources(Config.IdentityResources)
            .AddInMemoryApiScopes(Config.ApiScopes)
            .AddInMemoryApiResources(Config.Apis)
            .AddInMemoryClients(Config.Clients)
            .AddJwtBearerClientAuthentication()
            ;

        // not recommended for production - you need to store your key material somewhere secure
        builder.AddDeveloperSigningCredential();
    }

    public void Configure(IApplicationBuilder app)
    {
        if (Environment.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        // uncomment if you want to add MVC
        app.UseStaticFiles();
        app.UseRouting();
        //-----------------------------------

        app.UseIdentityServer();

        // uncomment, if you want to add MVC
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
        });
        //-----------------------------------
    }
}
Config.cs

public static class Config
{
    public static IEnumerable<IdentityResource> IdentityResources =>
        new IdentityResource[]
        { 
            new IdentityResources.OpenId()
        };

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

    public static IEnumerable<ApiScope> ApiScopes =>
        new ApiScope[]
        { 
            new ApiScope() {
                Description = "An example scope",
                DisplayName = "api1",
                Enabled = true,
                Name = "api1",
                ShowInDiscoveryDocument = true,
                UserClaims = new string[] {"UserClaim1", "UserClaim2"}
            }
        };

    public static IEnumerable<Client> Clients =>
        new 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" }
            }
        };
}
输出为:

============================================================================================
Discovery Document:
============================================================================================
{"Policy":{"LoopbackAddresses":["localhost","127.0.0.1"],"Authority":"https://localhost:5001","AuthorityValidationStrategy":{},"RequireHttps":true,"AllowHttpOnLoopback":true,"ValidateIssuerName":true,"ValidateEndpoints":true,"EndpointValidationExcludeList":[],"AdditionalEndpointBaseAddresses":[],"RequireKeySet":true},"KeySet":{"Keys":[{"alg":"RS256","e":"AQAB","key_ops":[],"kid":"57EDAEBEC68F3CAACE869E3FA226C0FF","kty":"RSA","n":"oFo6iB0Kd-wzEFeR-fY12_8cF2uirsHI5FAtTAAOlAWUm5MRIPJjpXy8D4R9ZjU5750JUqcotQii8YF4DP_lN8Ro3SKFtI9HD4IazsX65ici2hhKSdAl4MEdUBRIgEdCwolQJgDOAhqls6WNqLRsh1Ify0EKI9AVKInwTbEXgCaHSsqGw8zubx8fSdQ4lgxQZGii792XYPVhFXMoom-6dVY9_7z5o5Or2sATdqaEAuLPLZLqMNVT284S9vMd4hxolIxVbuRgKQV4MZ-1mBK_C-GqjishVxdew6d_GasmRAt_2s0R4JlgZgeqzd7U2Agu5RETxpv6WUiDC9qCZnmXjQ","use":"sig","x5c":[],"KeySize":2048,"HasPrivateKey":false}]},"Issuer":"https://localhost:5001","AuthorizeEndpoint":"https://localhost:5001/connect/authorize","TokenEndpoint":"https://localhost:5001/connect/token","UserInfoEndpoint":"https://localhost:5001/connect/userinfo","IntrospectionEndpoint":"https://localhost:5001/connect/introspect","RevocationEndpoint":"https://localhost:5001/connect/revocation","DeviceAuthorizationEndpoint":"https://localhost:5001/connect/deviceauthorization","JwksUri":"https://localhost:5001/.well-known/openid-configuration/jwks","EndSessionEndpoint":"https://localhost:5001/connect/endsession","CheckSessionIframe":"https://localhost:5001/connect/checksession","RegistrationEndpoint":null,"FrontChannelLogoutSupported":true,"FrontChannelLogoutSessionSupported":true,"GrantTypesSupported":["authorization_code","client_credentials","refresh_token","implicit","urn:ietf:params:oauth:grant-type:device_code"],"CodeChallengeMethodsSupported":["plain","S256"],"ScopesSupported":["openid","api1","offline_access"],"SubjectTypesSupported":["public"],"ResponseModesSupported":["form_post","query","fragment"],"ResponseTypesSupported":["code","token","id_token","id_token token","code id_token","code token","code id_token token"],"ClaimsSupported":["sub","UserClaim1","UserClaim2"],"TokenEndpointAuthenticationMethodsSupported":["client_secret_basic","client_secret_post","private_key_jwt"],"HttpResponse":{"Version":"1.1","Content":{"Headers":[{"Key":"Content-Type","Value":["application/json; charset=UTF-8"]}]},"StatusCode":200,"ReasonPhrase":"OK","Headers":[{"Key":"Date","Value":["Mon, 29 Jun 2020 20:14:49 GMT"]},{"Key":"Server","Value":["Kestrel"]},{"Key":"Transfer-Encoding","Value":["chunked"]}],"TrailingHeaders":[],"RequestMessage":{"Address":"https://localhost:5001","ClientId":null,"ClientSecret":null,"ClientAssertion":{"Type":null,"Value":null},"ClientCredentialStyle":1,"AuthorizationHeaderStyle":0,"Parameters":{},"Version":"1.1","Content":null,"Method":{"Method":"GET"},"RequestUri":"https://localhost:5001/.well-known/openid-configuration","Headers":[{"Key":"Accept","Value":["application/json"]}],"Properties":{}},"IsSuccessStatusCode":true},"Raw":"{\"issuer\":\"https://localhost:5001\",\"jwks_uri\":\"https://localhost:5001/.well-known/openid-configuration/jwks\",\"authorization_endpoint\":\"https://localhost:5001/connect/authorize\",\"token_endpoint\":\"https://localhost:5001/connect/token\",\"userinfo_endpoint\":\"https://localhost:5001/connect/userinfo\",\"end_session_endpoint\":\"https://localhost:5001/connect/endsession\",\"check_session_iframe\":\"https://localhost:5001/connect/checksession\",\"revocation_endpoint\":\"https://localhost:5001/connect/revocation\",\"introspection_endpoint\":\"https://localhost:5001/connect/introspect\",\"device_authorization_endpoint\":\"https://localhost:5001/connect/deviceauthorization\",\"frontchannel_logout_supported\":true,\"frontchannel_logout_session_supported\":true,\"backchannel_logout_supported\":true,\"backchannel_logout_session_supported\":true,\"scopes_supported\":[\"openid\",\"api1\",\"offline_access\"],\"claims_supported\":[\"sub\",\"UserClaim1\",\"UserClaim2\"],\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"implicit\",\"urn:ietf:params:oauth:grant-type:device_code\"],\"response_types_supported\":[\"code\",\"token\",\"id_token\",\"id_token token\",\"code id_token\",\"code token\",\"code id_token token\"],\"response_modes_supported\":[\"form_post\",\"query\",\"fragment\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"private_key_jwt\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"subject_types_supported\":[\"public\"],\"code_challenge_methods_supported\":[\"plain\",\"S256\"],\"request_parameter_supported\":true}","Json":{"issuer":"https://localhost:5001","jwks_uri":"https://localhost:5001/.well-known/openid-configuration/jwks","authorization_endpoint":"https://localhost:5001/connect/authorize","token_endpoint":"https://localhost:5001/connect/token","userinfo_endpoint":"https://localhost:5001/connect/userinfo","end_session_endpoint":"https://localhost:5001/connect/endsession","check_session_iframe":"https://localhost:5001/connect/checksession","revocation_endpoint":"https://localhost:5001/connect/revocation","introspection_endpoint":"https://localhost:5001/connect/introspect","device_authorization_endpoint":"https://localhost:5001/connect/deviceauthorization","frontchannel_logout_supported":true,"frontchannel_logout_session_supported":true,"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"scopes_supported":["openid","api1","offline_access"],"claims_supported":["sub","UserClaim1","UserClaim2"],"grant_types_supported":["authorization_code","client_credentials","refresh_token","implicit","urn:ietf:params:oauth:grant-type:device_code"],"response_types_supported":["code","token","id_token","id_token token","code id_token","code token","code id_token token"],"response_modes_supported":["form_post","query","fragment"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","private_key_jwt"],"id_token_signing_alg_values_supported":["RS256"],"subject_types_supported":["public"],"code_challenge_methods_supported":["plain","S256"],"request_parameter_supported":true},"Exception":null,"IsError":false,"ErrorType":0,"HttpStatusCode":200,"HttpErrorReason":"OK","Error":null}
============================================================================================
Token Response:
============================================================================================
{
  "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjU3RURBRUJFQzY4RjNDQUFDRTg2OUUzRkEyMjZDMEZGIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE1OTM0NjE2OTAsImV4cCI6MTU5MzQ2NTI5MCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NTAwMSIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjUwMDEvcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoiY2xpZW50IiwianRpIjoiNEY5QzczMDZBRjdFMURDNjI3QkRBQTdCRjg4MjlDNTMiLCJpYXQiOjE1OTM0NjE2OTAsInNjb3BlIjpbImFwaTEiXX0.FxvjG89zv1a83MtyjJvzCA26g_VLO6HTElJuOSi1FOp_My1RGHB-mbg53E6jZF9Xq_pkAOak5SC73tMC0b3hcEGx9O1qsd9c_Q9ish2ffmCZZ34svkpsfZp3wjbS-xNyxq7mjSOg0JGpf3ML_eUz3TUcOa5Aba_evzmRDaVgAvEtsdM8D7lK_udnQmw0cDimc8vYaGSLIXJDfOhM9pb-8I67deElCxaIEG93CwRZV5bwQQQC3dLwihb51wndv962Kw0dPkIXrt1n7jwEQ4KAhBqVcP9DAgPTqem1Kix8Uq_P4wBTm_cMY7U7bCa-j6mvRZ8t7TxWARpylzlL-ojy7g",
  "expires_in": 3600,
  "token_type": "Bearer",
  "scope": "api1"
}
============================================================================================
Calling api endpoint https://localhost:6001/MyEndpoint/Get
============================================================================================
============================================================================================
Request
============================================================================================
Request:
Method: GET, RequestUri: 'https://localhost:6001/MyEndpoint/Get', Version: 1.1, Content: <null>, Headers:
{
  Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjU3RURBRUJFQzY4RjNDQUFDRTg2OUUzRkEyMjZDMEZGIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE1OTM0NjE2OTAsImV4cCI6MTU5MzQ2NTI5MCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NTAwMSIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjUwMDEvcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoiY2xpZW50IiwianRpIjoiNEY5QzczMDZBRjdFMURDNjI3QkRBQTdCRjg4MjlDNTMiLCJpYXQiOjE1OTM0NjE2OTAsInNjb3BlIjpbImFwaTEiXX0.FxvjG89zv1a83MtyjJvzCA26g_VLO6HTElJuOSi1FOp_My1RGHB-mbg53E6jZF9Xq_pkAOak5SC73tMC0b3hcEGx9O1qsd9c_Q9ish2ffmCZZ34svkpsfZp3wjbS-xNyxq7mjSOg0JGpf3ML_eUz3TUcOa5Aba_evzmRDaVgAvEtsdM8D7lK_udnQmw0cDimc8vYaGSLIXJDfOhM9pb-8I67deElCxaIEG93CwRZV5bwQQQC3dLwihb51wndv962Kw0dPkIXrt1n7jwEQ4KAhBqVcP9DAgPTqem1Kix8Uq_P4wBTm_cMY7U7bCa-j6mvRZ8t7TxWARpylzlL-ojy7g
}

Response:
StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
  Date: Mon, 29 Jun 2020 20:14:50 GMT
  Server: Kestrel
  WWW-Authenticate: Bearer error="invalid_token", error_description="The audience 'https://localhost:5001/resources' is invalid"
  Content-Length: 0
}


============================================================================================
Response
============================================================================================
Unauthorized

好的,我找到了修复此错误的方法,但它会带来其他问题

首先,查看生成的JWT令牌。它向观众展示的是“https://localhost:5001/resources". 因此,我扫描了代码,查找执行受众验证的位置,该验证位于通过IS4的Startup.cs文件对调用方进行身份验证的API中。将“受众”值更改为预期值,并成功:

        services.AddAuthentication("Bearer")
            .AddJwtBearer("Bearer", options =>
            {
                // Use our IS4 implementation as the authentication source.
                options.Authority = "https://localhost:5001";
                options.RequireHttpsMetadata = false;

                //options.Audience = "api1";
                options.Audience = "https://localhost:5001/resources";
            });

但是,这并没有告诉我该值是如何生成的或在何处重写它,而是解决了问题。

根据API上的当前设置,您需要的是access\u令牌中的
aud
as
api1
。要修复此问题,只需将作用域添加到API资源。验证上生成的令牌


publicstaticienumerable

我也遇到了同样的问题,并找到了解决方法。在Identity server 4配置中,必须使用要使用的名称添加作用域

下面是如何声明api的示例

public static IEnumerable<ApiResource> Apis =>
                    new ApiResource[]
                    {
                         new ApiResource("adminApi", "Admin Panel Service")
                         {
                             Scopes = {
                                 "adminApi"
                             }
                         }
                    };
公共静态IEnumerable API=>
新资源[]
{
新的API资源(“adminApi”、“管理面板服务”)
{
作用域={
“adminApi”
}
}
};
并将其添加到您的配置中

var builder = services.AddIdentityServer(options =>
            {
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;

                // see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
                options.EmitStaticAudienceClaim = true;
            })
                .AddInMemoryIdentityResources(Config.IdentityResources)
                .AddInMemoryApiResources(Config.Apis)
                .AddInMemoryApiScopes(Config.ApiScopes)
                .AddInMemoryClients(Config.Clients)
                .AddAspNetIdentity<ApplicationUser>()
                .AddJwtBearerClientAuthentication();

var builder=services.AddIdentityServer(选项=>
{
options.Events.RaiseErrorEvents=true;
options.Events.RaiseInformationEvents=true;
options.Events.RaiseFailureEvents=true;
options.Events.RaiseSuccessEvents=true;
//看https://identityserver4.readthedocs.io/en/latest/topics/resources.html
options.EmitStaticAudienceClaim=true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiResources(Config.api)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
.AddAsNetIdentity()
.AddJwtBearerClientAuthentication();

据我所知,文档中的值为“'https://localhost:5001/resources“当您使用
options.EmitStaticAudienceClaim=true时生成

如果您需要aud索赔,您可以启用 设置选项。这将在未来发出aud索赔 发卡机构名称/资源格式。如果你需要更多的澳元控制权 声明,使用API资源

我在这里找到的:

要在代币的“aud”部分包含“api1”,您需要遵循nahidf和Vesko I的建议:

在Config.cs文件中添加ApiResource:

public static IEnumerable<ApiResource> ApiResources =>
        new ApiResource[]
        {
            new ApiResource("api1", "Test API")
            {
                Scopes = { "api1.read", "api1.write" }
            }
        };
执行此操作后,Identity Server将创建包含以下内容的令牌:

"aud": [
   "api1",
   "https://localhost:44300/resources"
],

调用API时是否可以共享IdentityServer日志?完成!更新了问题。我的理解是,资源受众就像一个默认的受众值。如果您(在IdentityServer4 v4.xx中)将您的APIscope与ApiResources连接起来,您将获得aud声明中列出的其他访问群体值。资源/受众似乎总是在场。
var builder = services.AddIdentityServer(options =>
            {
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;

                // see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
                options.EmitStaticAudienceClaim = true;
            })
                .AddInMemoryIdentityResources(Config.IdentityResources)
                .AddInMemoryApiResources(Config.Apis)
                .AddInMemoryApiScopes(Config.ApiScopes)
                .AddInMemoryClients(Config.Clients)
                .AddAspNetIdentity<ApplicationUser>()
                .AddJwtBearerClientAuthentication();

public static IEnumerable<ApiResource> ApiResources =>
        new ApiResource[]
        {
            new ApiResource("api1", "Test API")
            {
                Scopes = { "api1.read", "api1.write" }
            }
        };
.AddInMemoryApiResources(Config.ApiResources)
"aud": [
   "api1",
   "https://localhost:44300/resources"
],