IdentityServer4:Quickstart始终返回未经授权的API端点,消息为;观众';https://localhost:5001/resources' “无效”;
按照逐字逐句的说明,我无法执行客户端。始终返回未经授权的401 生成的令牌是:IdentityServer4:Quickstart始终返回未经授权的API端点,消息为;观众';https://localhost:5001/resources' “无效”;,identityserver4,Identityserver4,按照逐字逐句的说明,我无法执行客户端。始终返回未经授权的401 生成的令牌是: { "alg": "RS256", "kid": "57EDAEBEC68F3CAACE869E3FA226C0FF", "typ": "at+jwt" }.{ "nbf": 1593466354, "exp": 1593469954,
{
"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
asapi1
。要修复此问题,只需将作用域添加到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"
],