Identityserver4 如何正确保护STS Asp.Net 2.2项目中的API+;Api Asp.Net 2.2项目+;angular7客户端
已经为此挣扎了一段时间,让angular7客户端访问id4 Asp.Net core 2.2项目以登录并获取jwt没有问题,让angular7客户端访问受id4保护的Asp.Net core 2.2 api项目保护的api也没有问题 jwt.io解码(值x'd out): ID4配置:Identityserver4 如何正确保护STS Asp.Net 2.2项目中的API+;Api Asp.Net 2.2项目+;angular7客户端,identityserver4,Identityserver4,已经为此挣扎了一段时间,让angular7客户端访问id4 Asp.Net core 2.2项目以登录并获取jwt没有问题,让angular7客户端访问受id4保护的Asp.Net core 2.2 api项目保护的api也没有问题 jwt.io解码(值x'd out): ID4配置: private static readonly string[] customClaimTypes = { "role", "jseg", "jobid", "regid", "api1" };
private static readonly string[] customClaimTypes = { "role", "jseg", "jobid", "regid", "api1" };
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResource("api1scope", customClaimTypes),
};
}
public static IEnumerable<ApiResource> GetApis()
{
return new ApiResource[]
{
new ApiResource()
{
Name = "api1",
Description = "tsicApis",
ApiSecrets =
{
new Secret(Startup.Configuration.GetSection("StsConfig:STSTSICApisSecuredSecret").Value.Sha256())
},
Scopes =
{
new Scope()
{
Name = "api1",
DisplayName = "Scope for the api1 ApiResource",
},
},
UserClaims = customClaimTypes
}
};
}
// clients want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
var trustedClientSecrets = Startup.Configuration.GetSection("StsConfig:TrustedClientSecrets").Value;
var angularClientUrl = Startup.Configuration.GetSection("StsConfig:AngularClientUrl").Value;
var angularRedirectUris = Startup.Configuration.GetSection("StsConfig:AngularRedirectUris").Value;
var angularPostLogoutRedirectUris = Startup.Configuration.GetSection("StsConfig:AngularPostLogoutRedirectUris").Value;
var angularAllowedCorsOrigins = Startup.Configuration.GetSection("StsConfig:AngularAllowedCorsOrigins").Value;
var angularClientSecret = Startup.Configuration.GetSection("StsConfig:STSTSICApisSecuredSecret").Value;
var mvcClientSecrets = Startup.Configuration.GetSection("StsConfig:MVCClientSecrets").Value;
var mvcRedirectUris = Startup.Configuration.GetSection("StsConfig:MVCRedirectUris").Value;
var mvcFrontChannelLogoutUri = Startup.Configuration.GetSection("StsConfig:MVCFrontChannelLogoutUri").Value;
var mvcPostLogoutRedirectUris = Startup.Configuration.GetSection("StsConfig:MVCPostLogoutRedirectUris").Value;
// client credentials client
return new List<Client>
{
new Client
{
ClientName = "angularclient",
ClientId = "angularclient",
RequireClientSecret = true,
ClientSecrets = { new Secret(angularClientSecret) },
RequireConsent = true,
AllowRememberConsent = false,
AccessTokenType = AccessTokenType.Jwt,
AlwaysIncludeUserClaimsInIdToken = true,
AccessTokenLifetime = 33000,// 330 seconds, default 60 minutes
IdentityTokenLifetime = 3000,
AllowAccessTokensViaBrowser = true,
AllowedGrantTypes = GrantTypes.Implicit,
AllowedCorsOrigins = angularAllowedCorsOrigins.Split(','),
AllowedScopes =
{
"openid",
"profile",
"email",
"role",
"jseg",
"jobid",
"regid",
"api1",
"api1scope",
},
RedirectUris = angularRedirectUris.Split(','),
PostLogoutRedirectUris = angularPostLogoutRedirectUris.Split(',')
},
new Client
{
ClientId = "mvcclient",
ClientName = "mvcclient",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets = { new Secret(mvcClientSecrets.Sha256()) },
RequireConsent = true,
AllowRememberConsent = false,
RedirectUris = mvcRedirectUris.Split(','),
FrontChannelLogoutUri = mvcFrontChannelLogoutUri,
PostLogoutRedirectUris = mvcPostLogoutRedirectUris.Split(','),
AllowOfflineAccess = true,
AllowedScopes = new List<string>
{
"openid",
"profile",
"api1"
}
},
};
}
Asp.Net core 2.2 api项目start.cs:
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServer4Strings:Authority");
options.RequireHttpsMetadata = Configuration.GetValue<bool>("IdentityServer4Strings:RequireHttpsMetadata");
options.ApiName = Configuration.GetValue<string>("IdentityServer4Strings:ApiName");
options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
options.ApiSecret = Configuration.GetValue<string>("IdentityServer4Strings:STSTSICApisSecuredSecret");
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
});
Asp.Net core 2.2 sts project start.cs:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var identityServer = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddProfileService<IdentityWithAdditionalClaimsProfileService>()
//.AddTestUsers(TestUsers.Users)
// this adds the config data from DB (clients, resources, CORS)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString);
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString);
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
})
.AddProfileService<IdentityWithAdditionalClaimsProfileService>()
.AddAspNetIdentity<ApplicationUser>();
services.AddTransient<IProfileService, IdentityWithAdditionalClaimsProfileService>();
Api authorize protection decorator (have tried both):
`
//[Authorize]
[Authorize(AuthenticationSchemes = "Bearer")]
`
Asp.Net core 2.2 sts start.cs:
`
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var identityServer = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddProfileService<IdentityWithAdditionalClaimsProfileService>()
//.AddTestUsers(TestUsers.Users)
// this adds the config data from DB (clients, resources, CORS)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString);
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString);
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
})
.AddProfileService<IdentityWithAdditionalClaimsProfileService>()
.AddAspNetIdentity<ApplicationUser>();
services.AddTransient<IProfileService, IdentityWithAdditionalClaimsProfileService>();
启动Asp.Net Core 2.2 sts项目时,我得到:
Seeding database...
Clients already populated
IdentityResources already populated
ApiResources already populated
Done seeding database.
[09:28:09 Information] IdentityServer4.Startup
Starting IdentityServer4 version 2.3.2.0
[09:28:09 Information] IdentityServer4.Startup
Using the default authentication scheme Identity.Application for IdentityServer
[09:28:09 Debug] IdentityServer4.Startup
Using Identity.Application as default ASP.NET Core scheme for authentication
[09:28:09 Debug] IdentityServer4.Startup
Using Identity.External as default ASP.NET Core scheme for sign-in
[09:28:09 Debug] IdentityServer4.Startup
Using Identity.External as default ASP.NET Core scheme for sign-out
[09:28:09 Debug] IdentityServer4.Startup
Using Identity.Application as default ASP.NET Core scheme for challenge
[09:28:09 Debug] IdentityServer4.Startup
Using Identity.Application as default ASP.NET Core scheme for forbid
[09:28:10 Debug] IdentityServer4.EntityFramework.TokenCleanup
Starting grant removal
Hosting environment: Development
Content root path: E:\Projects-STS\TSIC\TSIC.STS
Now listening on: https://localhost:44340
Application started. Press Ctrl+C to shut down.
[09:28:13 Debug] IdentityServer4.Startup
Login Url: /Account/Login
[09:28:13 Debug] IdentityServer4.Startup
Login Return Url Parameter: ReturnUrl
[09:28:13 Debug] IdentityServer4.Startup
Logout Url: /Account/Logout
[09:28:13 Debug] IdentityServer4.Startup
ConsentUrl Url: /consent
[09:28:13 Debug] IdentityServer4.Startup
Consent Return Url Parameter: returnUrl
[09:28:13 Debug] IdentityServer4.Startup
Error Url: /home/error
[09:28:13 Debug] IdentityServer4.Startup
Error Id Parameter: errorId
[09:28:25 Debug] IdentityServer4.Hosting.EndpointRouter
Request path /connect/authorize matched to endpoint type Authorize
[09:28:25 Debug] IdentityServer4.Hosting.EndpointRouter
Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeEndpoint
[09:28:25 Information] IdentityServer4.Hosting.IdentityServerMiddleware
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
[09:28:25 Debug] IdentityServer4.Endpoints.AuthorizeEndpoint
Start authorize request
[09:28:25 Debug] IdentityServer4.Endpoints.AuthorizeEndpoint
No user present in authorize request
[09:28:25 Debug] IdentityServer4.Validation.AuthorizeRequestValidator
Start authorize request protocol validation
[09:28:26 Debug] IdentityServer4.EntityFramework.Stores.ClientStore
angularclient found in database: True
[09:28:26 Debug] IdentityServer4.Stores.ValidatingClientStore
client configuration validation for client angularclient succeeded.
[09:28:27 Debug] IdentityServer4.EntityFramework.Stores.ResourceStore
Found ["openid", "profile", "email", "api1scope"] identity scopes in database
[09:28:27 Debug] IdentityServer4.EntityFramework.Stores.ResourceStore
Found [] API scopes in database
[09:28:27 Debug] IdentityServer4.EntityFramework.Stores.ResourceStore
Found ["openid", "profile", "email", "api1scope"] identity scopes in database
[09:28:27 Debug] IdentityServer4.EntityFramework.Stores.ResourceStore
Found [] API scopes in database
我想我离这里很近,只需要往正确的方向推一下
我目前的重点是:
[09:28:27 Debug] IdentityServer4.EntityFramework.Stores.ResourceStore
Found [] API scopes in database
认为这与api项目错误有关:
IDX10214: Audience validation failed. Audiences: 'https://localhost:44340/resources'. Did not match: validationParameters.ValidAudience: 'api1' or validationParameters.ValidAudiences: 'null'.
这让我感到很不舒服,因为数据库的dbo.ApiScopes中确实有一个来自SQL Server的条目:
Id Name DisplayName Description Required Emphasize ShowInDiscoveryDocument ApiResourceId
9 api1 Scope for the api1 ApiResource NULL 0 0 1 12
我非常感谢您对我的帮助:
openIDImplicitFlowConfiguration.scope = 'openid profile email api1scope';
但是,它应该与api1的一个有效作用域相匹配:
openIDImplicitFlowConfiguration.scope = 'openid profile email api1';
如果您请求至少一个属于给定Api的作用域,并且您的客户端允许该作用域,则Identity Server 4仅将Api资源添加为有效的令牌访问群体。在您的配置中,您有:
openIDImplicitFlowConfiguration.scope = 'openid profile email api1scope';
但是,它应该与api1的一个有效作用域相匹配:
openIDImplicitFlowConfiguration.scope = 'openid profile email api1';
如果您请求至少一个属于给定Api的作用域,并且您的客户端允许该作用域,则Identity Server 4仅将Api资源添加为有效的令牌访问群体。Vidmantas,感谢您的响应,我尝试了该操作,但出现了相同的错误。然后我幸运地解决了(是id令牌和访问令牌中指示的不同受众的问题,以及Api项目startup.cs中的IdentityServer4配置如何设置id令牌aud:)的问题: 明白了,希望这能帮助其他人: 返回登录后的angular客户端: id令牌:
{
"nbf": 1550240640,
"exp": 1550273640,
"iss": "https://localhost:44340",
"aud": "https://localhost:44340/resources",
"client_id": "angularclient",
"sub": "71765055-647D-432E-AFB6-0F84218D0247",
"auth_time": 1550240638,
"idp": "local",
"regid": "xxxx",
"jseg": "xxxxx",
"jobid": "b0984a87-172a-436e-a382-e95de3e1059f",
"role": "xxxx",
"given_name": "xxxxx",
"family_name": "xxxx",
"email": "xxxx",
"scope": [
"openid",
"profile",
"email"
],
"amr": [
"pwd"
]
}
和访问令牌:
{
"nbf": 1550240640,
"exp": 1550243640,
"iss": "https://localhost:44340",
"aud": "angularclient",
"nonce": "N0.55036966062308791550240634889",
"iat": 1550240640,
"at_hash": "yNVxDVHkmEmUvurl7XlzuA",
"sid": "f54dee03793e7cc202b57f1d6de7622e",
"sub": "71765055-647D-432E-AFB6-0F84218D0247",
"auth_time": 1550240638,
"idp": "local",
"preferred_username": "TSICSuperUser",
"name": "xxxx",
"email": "xxxxx",
"email_verified": true,
"regid": "xxxxx",
"jseg": "xxxxx",
"jobid": "xxxxxxf",
"role": "xxxxx",
"given_name": "xxxx",
"family_name": "xxxxx",
"amr": [
"pwd"
]
}
注意不同的受众(澳元:)
Asp.Net Core 2.2 Api项目startup.cs配置了IdentityServer 4:
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServer4Strings:Authority");
options.RequireHttpsMetadata = Configuration.GetValue<bool>("IdentityServer4Strings:RequireHttpsMetadata");
options.ApiName = "api1";
options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
options.ApiSecret = Configuration.GetValue<string>("IdentityServer4Strings:STSTSICApisSecuredSecret");
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
});
错误的“api1”:
不匹配:validationParameters.ValidAudience:'api1'
参考startup.cs
.AddIdentityServerAuthentication
options.ApiName = "api1";
将Asp.Net Core 2.2 Api项目startup.cs更改为:
// critical for bearer authentication, the audience of the id token (set by Options.ApiName) is equal to this value
var idTokenAudience = $"{Configuration.GetValue<string>("IdentityServer4Strings:Authority")}/resources";
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServer4Strings:Authority");
options.RequireHttpsMetadata = Configuration.GetValue<bool>("IdentityServer4Strings:RequireHttpsMetadata");
options.ApiName = idTokenAudience;
options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
options.ApiSecret = Configuration.GetValue<string>("IdentityServer4Strings:STSTSICApisSecuredSecret");
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
});
维德曼塔斯,谢谢你的回复,我试过了,同样的错误也发生了。然后我幸运地解决了(是id令牌和访问令牌中指示的不同受众的问题,以及Api项目startup.cs中的IdentityServer4配置如何设置id令牌aud:)的问题: 明白了,希望这能帮助其他人: 返回登录后的angular客户端: id令牌:
{
"nbf": 1550240640,
"exp": 1550273640,
"iss": "https://localhost:44340",
"aud": "https://localhost:44340/resources",
"client_id": "angularclient",
"sub": "71765055-647D-432E-AFB6-0F84218D0247",
"auth_time": 1550240638,
"idp": "local",
"regid": "xxxx",
"jseg": "xxxxx",
"jobid": "b0984a87-172a-436e-a382-e95de3e1059f",
"role": "xxxx",
"given_name": "xxxxx",
"family_name": "xxxx",
"email": "xxxx",
"scope": [
"openid",
"profile",
"email"
],
"amr": [
"pwd"
]
}
和访问令牌:
{
"nbf": 1550240640,
"exp": 1550243640,
"iss": "https://localhost:44340",
"aud": "angularclient",
"nonce": "N0.55036966062308791550240634889",
"iat": 1550240640,
"at_hash": "yNVxDVHkmEmUvurl7XlzuA",
"sid": "f54dee03793e7cc202b57f1d6de7622e",
"sub": "71765055-647D-432E-AFB6-0F84218D0247",
"auth_time": 1550240638,
"idp": "local",
"preferred_username": "TSICSuperUser",
"name": "xxxx",
"email": "xxxxx",
"email_verified": true,
"regid": "xxxxx",
"jseg": "xxxxx",
"jobid": "xxxxxxf",
"role": "xxxxx",
"given_name": "xxxx",
"family_name": "xxxxx",
"amr": [
"pwd"
]
}
注意不同的受众(澳元:)
Asp.Net Core 2.2 Api项目startup.cs配置了IdentityServer 4:
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServer4Strings:Authority");
options.RequireHttpsMetadata = Configuration.GetValue<bool>("IdentityServer4Strings:RequireHttpsMetadata");
options.ApiName = "api1";
options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
options.ApiSecret = Configuration.GetValue<string>("IdentityServer4Strings:STSTSICApisSecuredSecret");
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
});
错误的“api1”:
不匹配:validationParameters.ValidAudience:'api1'
参考startup.cs
.AddIdentityServerAuthentication
options.ApiName = "api1";
将Asp.Net Core 2.2 Api项目startup.cs更改为:
// critical for bearer authentication, the audience of the id token (set by Options.ApiName) is equal to this value
var idTokenAudience = $"{Configuration.GetValue<string>("IdentityServer4Strings:Authority")}/resources";
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServer4Strings:Authority");
options.RequireHttpsMetadata = Configuration.GetValue<bool>("IdentityServer4Strings:RequireHttpsMetadata");
options.ApiName = idTokenAudience;
options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
options.ApiSecret = Configuration.GetValue<string>("IdentityServer4Strings:STSTSICApisSecuredSecret");
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
});
我第一眼看到两件事。首先-api作用域不是标识资源。把它从那里拿走。在Angular module.ts中,第二个将作用域固定为api1,而不是api1scope。然后-确保
IdentityServer4Strings:Authority
和传递给angular应用程序的值(environment.oidc.stsServer
)相同。该机构https://localhost:44340/resources
出现错误似乎很奇怪。我第一眼看到两件事。首先-api作用域不是标识资源。把它从那里拿走。在Angular module.ts中,第二个将作用域固定为api1,而不是api1scope。然后-确保IdentityServer4Strings:Authority
和传递给angular应用程序的值(environment.oidc.stsServer
)相同。该机构https://localhost:44340/resources
您遇到的错误似乎很奇怪。