一. 前言
IdentityServer4 实战这个系列主要介绍一些在 IdentityServer4(后文称: ids4), 在实际使用过程中容易出现的问题, 以及使用技巧, 不定期更新, 谢谢大家关注. 使用过 ids4 的朋友应该知道, 可以通过设置
AccessTokenLifetime
属性, 来控制 AccessToken 的存活时间, 但是细心的朋友可能会发现, Token 到期了依然能通过授权, 这是怎么回事呢, 下面我带大家一起来揭开神秘面纱.
二. 关于 ID Token 和 AccessToken
Openid Connect(后文称: OIDC)是在 OAuth2.0 协议上进行了扩展, IODC=Identity+OAuth2.0, 使其拥有身份认证 + 授权的能. 它在 OAuth2 上构建了一个身份层, 是一个基于 OAuth2 协议的身份认证标准协议. 我们都知道 OAuth2 是一个授权协议, 它无法提供完善的身份认证功能, OIDC 使用 OAuth2 的授权服务器来为第三方客户端提供用户的身份认证, 并把对应的身份认证信息传递给客户端, 且可以适用于各种类型的客户端(比如服务端应用, 移动 APP,JS 应用), 且完全兼容 OAuth2, 也就是说你搭建了一个 OIDC 的服务后, 也可以当作一个 OAuth2 的服务来用. 应用场景如图:
OAuth2 提供了 Access Token 来解决授权第三方客户端访问受保护资源的问题; OIDC 在这个基础上提供了 ID Token 来解决第三方客户端标识用户身份认证的问题. OIDC 的核心在于在 OAuth2 的授权流程中, 一并提供用户的身份认证信息 (ID Token) 给到第三方客户端, ID Token 使用 JWT 格式来包装, 得益于 JWT(JSON web Token)的自包含性, 紧凑性以及防篡改机制, 使得 ID Token 可以安全的传递给第三方客户端程序并且容易被验证. 此外还提供了 UserInfo 的接口, 用户获取用户的更完整的信息.
简而言之 ID Token 就是 JWT 格式的数据, 包含一个人类用户的身份认证的信息, 一个 ID Token 的例子如下:
- {
- "iss": "https://server.example.com",
- "sub": "24400320",
- "aud": "s6BhdRkqt3",
- "nonce": "n-0S6_WzA2Mj",
- "exp": 1311281970,
- "iat": 1311280970,
- "auth_time": 1311280969,
- "acr": "urn:mace:incommon:iap:silver"
- }
看到上面的数据是不是感觉很熟悉, 这是一个我们从 ids4 申请的 "AccessToken":
eyJhbGciOiJSUzI1NiIsImtpZCI6IjhlM2U2MWY1ZWUyZDgwMGNlYjE2NmE5NGRjODczMTY0IiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MjU1Nzg3MTUsImV4cCI6MTUyNTU3ODcxNiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6NTAwMC9yZXNvdXJjZXMiLCJhcGkxIl0sImNsaWVudF9pZCI6InJvLmNsaWVudCIsInN1YiI6IjEiLCJhdXRoX3RpbWUiOjE1MjU1Nzg3MTUsImlkcCI6ImxvY2FsIiwic2NvcGUiOlsiYXBpMSJdLCJhbXIiOlsicHdkIl19.JXU4bXUqf8QD4zQz61XC2WTKURtNIVhH23zQPJzOmEtYbQvO2oRP58sCfDQxADeImZ7O0vH4YXIfL8j60B-sAYJev7c2hnjVhHTJ0t-0bUPlLs43cqNG6RarZ8FyfHyhrvIwYBpJXKNROfr6GfLb4Vdpw4ZEd4AC2k2tHuKMfyrrTzqS0oUs1RwqH7KZ1W7pXDr_V2L4PjgCqOQelXAB_V5YXzR9E52FIXnKNzCVnWHmhiTSWg-ptONOoHss1a-ElWejXskTlMBQitnxSno05s4O6vp5R8zqMuo3j57SnPZVaTuR4AUVpDdVmFF9x9k-fHuXyqarsW6YGsXgTTA2Lw
我们将上面的 Token 解码可以看见:
我想不用我多说, 就可以看见我们的 ID Token 在哪里吧.
三. 设置 AccessToken 过期时间
我们在 ids 端设置我们的客户端资源的时候有一个
AccessTokenLifetime
属性, 此属性可以设置我们申请的 Token 的有效时间, 单位是秒, 默认 3600 秒也就是一个小时.
代码示例:
- new Client
- {
- ClientId = "ro.client",
- AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
- AccessTokenLifetime = 5,
- ClientSecrets =
- {
- new Secret("secret".Sha256())
- },
- AllowedScopes = { "api1" }
- }
本文所用代码, 为 ids4 文档中, 第二个 QuickStart, 文末会给出地址.
Client 对象还包含一个
IdentityTokenLifetime
属性, 是用来设置 ID Token 的存活时间, 但是, 我们获得 AccessToken 以后, 访问 API 资源等, 是用的授权, 此属性无法影响 AccessToken 的有效期, 这也是我在上面解释了 ID Token 和 AccessToken 的区别的原因, 希望大家不要搞混淆了.
我们上面将 AccessToken 的存活时间设置为 5s, 我们修改客户端的代码改一下, 让他暂停 6s 再次去访问 APi 资源, 看看会发生什么:
我们先看看返回的 AccessToken 信息里, 过期时间已经变成了 5s:
看看暂停之后的结果:
可以看到, 本来 Token 应该过期无法访问的, 但是还是成功访问 API 获取到了信息:
这是怎么回事呢, 和我们想的有点不一样, 请听下节分解!
四. 时间偏移(ClockSkew )
有这样的场景, 如果你的 AccessToken 还有 5s 过期, 这时你通过这个 AccessToken 去访问 API 资源, 但是这时网络堵塞, 可能请求 10s 才到达目标, 那这时怎么办? 如果需要保持所持有的 AccessToken 一直有效, 是否需要提前刷新或者再次申请 AccessToken? 如果你本地的时间和 API 资源服务的时间具有时间差异可能是几秒, 几十秒等等, 那么你该如何判断你所持有的 AccessToken 的有效性?
上述的这些问题, 都是我们将时间理想化了, 所以当我们的 API 资源受到请求根据 AccessToken 进行验证的时候, 会有一个时间偏移, 通俗的讲就是将 AccessToken 的逻辑过期时间往后推迟了, 这个时间默认是 5 分钟. 比如我们的 AccessToken 应该在
2018 年 5 月 6 日 16:50:55
过期, 那么实际上在 API 资源进行验证的时候, 容忍在过期时间后的五分钟以内, 此 AccessToken 依然是有效的, 即在 API 资源验证时, 此 AccessToken 的真正过期时间为
2018 年 5 月 6 日 16:55:55
, 这个时间差就是用来解决上述问题的. 这也是为什么上面我们将 AccessToken 设置 5s 过期, 但实际上 5s 之后还能用它成功访问 API.
此设置是针对于 JWT 的, 这里需要注意.
五. 设置时间偏移
1. 获取默认过期时间
默认过期时间我们可以通过 JwtBearerOptions 对象的
TokenValidationParameters
属性的 ClockSkew 属性来获取.
可以看见默认的时间偏移为 5 分钟, 那么如何来自定义这个值呢.
2. 设置时间偏移
我们可以通过
IdentityServerAuthenticationOptions
对象提供
JwtValidationClockSkew
属性来自定义时间偏移, 这个设置是在 API 资源的, 因为当我们请求 API 资源的时候, 是 API 资源自行进行验证的.
代码如下:
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddMvcCore()
- .AddAuthorization()
- .AddJsonFormatters();
- services.AddAuthentication("Bearer")
- .AddIdentityServerAuthentication(options =>
- {
- options.Authority = "http://localhost:5000";
- options.RequireHttpsMetadata = false;
- options.JwtValidationClockSkew = TimeSpan.FromSeconds(0);
- options.ApiName = "api1";
- });
- }
我们通过
options.JwtValidationClockSkew = TimeSpan.FromSeconds(0);
将这个时间偏移设置为了 0s, 那么我们现在再运行我们前面的程序, 设置 AccessToken 过期时间为 5s, 我们暂停 6s, 会发生什么.
可以看到提示 "Unauthorized", 可以看到现在的情况和我们前面所想的情况一致了. 这就是时间偏移的作用.
六. 写在最后
在实际生产环境中, 一定要尽量保持各个服务, 各个节点的时间同步, 使用标准时间. 然后这个时间偏移如没有特殊需求不建议去更改它, 这个就是这样设计的, 官方也是不推荐去更改它. 如果设置过短可能引起文章说的问题哦. 欢迎大家加入 QQ 群 (4656606) 和我一起交流, 写本文也是群里许多朋友问过这个问题, 以前一直没注意, 今天才算解开了它的秘密.
本文所用代码下载:
https://github.com/stulzq/BlogDemos/tree/master/ids4AccessTokenLifetime
参考资料:
[认证授权] 4.OIDC(OpenId Connect)身份认证授权(核心部分)
IdentityServer4 issues https://github.com/IdentityServer/IdentityServer4/issues/497
来源: https://www.cnblogs.com/stulzq/p/8998274.html