在使用IdentityServer4进行测试时,我发现了一种“奇怪”的行为。
我的API无法检测到过期的令牌。
设置:
在IdentiyServer4端,我的cmd客户端的AccessTokenLifetime设置为20秒。
签入客户端时,我可以看到JWT令牌的exp
值已正确设置为now
+ 20秒。
API接受带有令牌(包括过期令牌)的来自客户端的所有调用。
我希望我的API仅接受有效/尚未过期的令牌。但这种情况并非如此。 我必须在我的API项目中启用/添加一些验证组件吗?
这是API的配置:
public class Startup
{
public void Configure( IApplicationBuilder app, IHostingEnvironment env )
{
if ( env.IsDevelopment() )
app.UseDeveloperExceptionPage();
app.UseAuthentication();
app.UseMvc();
}
public void ConfigureServices( IServiceCollection services )
{
services.AddMvc();
services.AddAuthorization();
services
.AddAuthentication( IdentityServerAuthenticationDefaults.AuthenticationScheme )
.AddIdentityServerAuthentication( options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "ApiPoc";
} );
}
}
IdentityServer-主机配置:
public class Startup
{
public void Configure( IApplicationBuilder app, IHostingEnvironment env )
{
if ( env.IsDevelopment() )
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
}
public void ConfigureServices( IServiceCollection services )
{
// IdentityServer 4
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources( Config.GetIdentityResources() ) //check below
.AddInMemoryApiResources( Config.GetApiResources() )
.AddInMemoryClients( Config.GetClients() )
.AddProfileService<ProfileService>();
// Test - custom user repo
services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
services.AddTransient<IProfileService, ProfileService>();
}
}
public class Config
{
public static IEnumerable<ApiResource> GetApiResources() =>
new List<ApiResource>
{
new ApiResource( "ApiPoc", "API PoC" )
};
public static IEnumerable<Client> GetClients() =>
new List<Client>
{
new Client
{
ClientId = "CmdClient",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret( "CmdClientSecret".Sha256() )
},
AllowedScopes = { "ApiPoc" },
AccessTokenLifetime = 20,
AccessTokenType = AccessTokenType.Jwt,
SlidingRefreshTokenLifetime = 1296000
}
};
public static IEnumerable<IdentityResource> GetIdentityResources() =>
new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
Cmd客户端代码:
private static async Task DoRequestInLoop()
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync( "http://localhost:5000" ).ConfigureAwait( false );
if ( disco.IsError )
{
Console.WriteLine( $"Failed to get discovery document: {disco.Error}" );
return;
}
var tokenResponse = await client.RequestPasswordTokenAsync( new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "CmdClient",
ClientSecret = "CmdClientSecret",
Scope = "ApiPoc",
Password = "user",
UserName = "password",
Parameters = new Dictionary<String, String>
{
{ "UserGroup", "A_Admin" },
{ "Tenant", "A" }
}
},
CancellationToken.None ).ConfigureAwait( false );
if ( tokenResponse.IsError )
{
Console.WriteLine( $"Failed to obtain token: {tokenResponse.Error}." );
return;
}
var json = tokenResponse.Json.ToString();
client.SetBearerToken( tokenResponse.AccessToken );
Console.WriteLine( $"Token: {json}" );
while ( !_loopCanceled )
{
var response = await client.GetAsync( "http://localhost:5001/api/Values" ).ConfigureAwait( false );
Console.WriteLine( !response.IsSuccessStatusCode ? $"Request failed with status code {response.StatusCode}" : "Request successful." );
await Task.Delay( 22000 ).ConfigureAwait( false );
}
}
客户端从IdentityServer收到的令牌(您可以看到它仅有效20秒,这是正确的)
{
"nbf": 1546243372,
"exp": 1546243392,
"iss": "http://localhost:5000",
"aud": [
"http://localhost:5000/resources",
"ApiPoc"
],
"client_id": "CmdClient",
"sub": "1",
"auth_time": 1546243372,
"idp": "local",
"SomeClaim": "Claim",
"scope": [
"ApiPoc"
],
"amr": [
"custom"
]
}