OpenID Connect(Core),OAuth 2.0(RFC 6749),JSON Web Token (JWT)(RFC 7519) 之间有着密不可分联系,对比了不一样语言的实现,仍是以为 IdentityServer4 设计的比较完美,最近把源码 clone 下来研究了一下,以前介绍过 IdentityServer4 相关的文章(ASP.NET Core 中集成 IdentityServer4 实现 OAuth 2.0 与 OIDC 服务),在配置 Client 客户端的时候 Token 的类型有两种,IdentityServer4 默认使用 JWT 类型。html
/// <summary> /// Access token types. /// </summary> public enum AccessTokenType { /// <summary> /// Self-contained Json Web Token /// </summary> Jwt = 0, /// <summary> /// Reference token /// </summary> Reference = 1 }
JWT 是一个很是轻巧的规范,通常被用来在身份提供者和服务提供者间传递安全可靠的信息。常被用于先后端分离,能够和 Restful API 配合使用,经常使用于构建身份认证机制,一个 JWT 实际上就是一个字符串,它包含了使用.分隔的三部分: Header 头部 Payload 负载 Signature 签名(格式:Header.Payload.Signature)node
载荷(Payload)git
Payload 被定义成一个 JSON 对象,也能够增长一些自定义的信息。github
{ "iss": "irving", "iat": 1891593502, "exp": 1891594722, "aud": "www.test.com", "sub": "root@test.com", "ext_age": "18" }
JWT 标准所定义字段web
将上面的 JSON 对象使用 Base64 编码获得的字符串就是 JWT 的 Payload(载荷),也能够自定义一些字段另外在载荷里面通常不要加入敏感的数据。算法
头部(Header)json
头部用于描述关于该 JWT 的最基本的信息,例如其类型以及签名所用的算法等后端
{ "typ": "JWT", "alg": "HS256" }
上述说明这是一个JWT,所用的签名算法是 HS256(HMAC-SHA256)。对它也要进行 Base64 编码,以后的字符串就成了 JWT 的 Header(头部),关于 alg 中定义的签名算法推荐使用 RSA 或 ECDSA 非对称加密算法 ,这部分在 JSON Web Algorithms (JWA)[RFC7518] 规范中能够找到。api
+--------------+-------------------------------+--------------------+ | "alg" Param | Digital Signature or MAC | Implementation | | Value | Algorithm | Requirements | +--------------+-------------------------------+--------------------+ | HS256 | HMAC using SHA-256 | Required | | HS384 | HMAC using SHA-384 | Optional | | HS512 | HMAC using SHA-512 | Optional | | RS256 | RSASSA-PKCS1-v1_5 using | Recommended | | | SHA-256 | | | RS384 | RSASSA-PKCS1-v1_5 using | Optional | | | SHA-384 | | | RS512 | RSASSA-PKCS1-v1_5 using | Optional | | | SHA-512 | | | ES256 | ECDSA using P-256 and SHA-256 | Recommended+ | | ES384 | ECDSA using P-384 and SHA-384 | Optional | | ES512 | ECDSA using P-521 and SHA-512 | Optional | | PS256 | RSASSA-PSS using SHA-256 and | Optional | | | MGF1 with SHA-256 | | | PS384 | RSASSA-PSS using SHA-384 and | Optional | | | MGF1 with SHA-384 | | | PS512 | RSASSA-PSS using SHA-512 and | Optional | | | MGF1 with SHA-512 | | | none | No digital signature or MAC | Optional | | | performed | | +--------------+-------------------------------+--------------------+
签名(Signature)缓存
签名还须要一个 secret ,通常保存在服务端,使用的是 HS256 算法,流程相似于:
header = '{"alg":"HS256","typ":"JWT"}' payload = '{"loggedInAs":"admin","iat":1422779638}' key = 'secretkey' unsignedToken = encodeBase64(header) + '.' + encodeBase64(payload) signature = HMAC-SHA256(key, unsignedToken) jwt_token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)
签名的过程,其实是对头部以及载荷内容进行签名,最后以 Header.Payload.Signature 方式拼接最终获得 JWT。
RSA仍是HMAC
HS256 使用密钥生成固定的签名,RS256 使用成非对称进行签名。简单地说,HS256 必须与任何想要验证 JWT的 客户端或 API 共享秘密。与任何其余对称算法同样,相同的秘密用于签名和验证 JWT。RS256 生成非对称签名,这意味着必须使用私钥来签签名 JWT,而且必须使用对应的公钥来验证签名。与对称算法不一样,使用 RS256 能够保证服务端是 JWT 的签名者,由于服务端是惟一拥有私钥的一方。这样作将再也不须要在许多应用程序之间共享私钥。使用 RS256 和 JWK 规范签名(JWS(JSON Web Signature),JWS 只是 JWT 的一种实现,除了 JWS 外,有 JWS, JWE, JWK, JWA 相关的规范)。
RS256与JWKS
上述说到由于 header 和 payload 是明文存储的,为了防止数据被修改,签名最好使用RS256(RSA 非对称加密,使用私钥签名)。JSON Web Key SET (JWKS) 定义了一组的JWK Set JSON 数据结构,JWKS 包含签名算法,证书的惟一标识(Kid)等信息,用于验证受权服务器发出的 JWT,通常从受权服务器中得到(IdentityServer4 的获取方式 /.well-known/openid-configuration/jwks)得到公钥。IdentityServer4 中使用是微软 System.IdentityModel.Tokens.Jwt 类库,采用 RS256 签名算法,使用 privatekey (保存在服务端)来签名 publickey 验签 。理论上由 IdentityServer4 生成的 JWT Token ,其余不一样的语言也可以去验签。
{ "keys": [ { "kty": "RSA", "use": "sig", "kid": "B4F7C5533A06B22E6D349BEFD84B76E730161B55", "x5t": "tPfFUzoGsi5tNJvv2Et25zAWG1U", "e": "AQAB", "n": "zDXSeNo4oO-Tn372eKUywF40D0HG4XXeYtbYtdnpVsIZkDDouZr2jFeq3C-AUb546CJXFqqZj6YZPOMtiHBfzyDGThd45mQvNwQ18B7lae4vab1hvxx9HZGku64Wy5JlqT2jHJ-WR7GS9OZjHSeioMoDE654LhDxJthfj_C2G0jA_RTnPQKnQgciv5JiENTUwrghr9cXzBNgPE0QLAhKrCEoVoSxYOWTL9EBCUc2DB2Vah7RHNfNItrXbrdqvrDQ5rXBH8Rq6irjSF_FjcuIwMkTmLOkswnC_qBN7qjbmgLRIxG3YiSnZR5bgyhjFWNzea0jmuWEiFIIIMwTfPXpPw", "x5c": [ "MIID8TCCAtmgAwIBAgIJAIRTKytMROvuMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYDVQQGEwJ6aDERMA8GA1UECAwIY2hpbmEICAgxETAPBgNVBAcMCHNoYW5naGFpMREwDwYDVQQKDAhob21laW5uczERMA8GA1UECwwIaG9tZWlubnMxDzANBgNVBAMMBmlydmluZzEiMCAGCSqGSIb3DQEJARYTeXR6aG91QGhvbWVpbm5zLmNvbTAeFw0xODA3MjUwNzI3MzZaFw0xOTA3MjUwNzI3MzZaMIGOMQswCQYDVQQGEwJ6aDERMA8GA1UECAwIY2hpbmEICAgxETAPBgNVBAcMCHNoYW5naGFpMREwDwYDVQQKDAhob21laW5uczERMA8GA1UECwwIaG9tZWlubnMxDzANBgNVBAMMBmlydmluZzEiMCAGCSqGSIb3DQEJARYTeXR6aG91QGhvbWVpbm5zLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMw10njaOKDvk59+9nilMsBeNA9BxuF13mLW2LXZ6VbCGZAw6Lma9oxXqtwvgFG+eOgiVxaqmY+mGTzjLYhwX88gxk4XeOZkLzcENfAe5WnuL2m9Yb8cfR2RpLuuFsuSZak9oxyflkexkvTmYx0noqDKAxOueC4Q8SbYX4/wthtIwP0U5z0Cp0IHIr+SYhDU1MK4Ia/XF8wTYDxNECwISqwhKFaEsWDlky/RAQlHNgwdlWoe0RzXzSLa1263ar6w0Oa1wR/Eauoq40hfxY3LiMDJE5izpLMJwv6gTe6o25oC0SMRt2Ikp2UeW4MoYxVjc3mtI5rlhIhSCCDME3z16T8CAwEAAaNQME4wHQYDVR0OBBYEFOK5Y2P7/L8KsOrPB+glPVkKi2VOMB8GA1UdIwQYMBaAFOK5Y2P7/L8KsOrPB+glPVkKi2VOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEnXXws/cBx5tA9cBfmkqGWzOU5/YmH9pzWchJ0ssggIqZVx0yd6ok7+C+2vKIRMp5E6GCfXWTB+LI7qjAVEvin1NwGZ06yNEsaYaJYMC/P/0TunoMEZmsLM3rk0aISbzkNciF+LVT16i0C+hT1+Pyr8lP4Ea1Uw0n50Np6SOwQ6e2PMFFOIaqjG94tuCN3RX819IJSQPbq9FtRmNvmbWPM1v2CO6SYT51SvsIHnZyn0rAK+h/hywVQqmI5ngi1nErIQEqybkZj00OhmYpAqsetWYU5Cs1qhJ70kktlrd+jMHdarVB9ko0h+ij6HL22mmBYAb7zVGWyDroNJVhEw6DA=" ], "alg": "RS256" } ] }
sig
represents signature
. 在 IdentityServer4 中的定义
/// <summary> /// Creates the JWK document. /// </summary> public virtual async Task<IEnumerable<Models.JsonWebKey>> CreateJwkDocumentAsync() { var webKeys = new List<Models.JsonWebKey>(); var signingCredentials = await Keys.GetSigningCredentialsAsync(); var algorithm = signingCredentials?.Algorithm ?? Constants.SigningAlgorithms.RSA_SHA_256; foreach (var key in await Keys.GetValidationKeysAsync()) { if (key is X509SecurityKey x509Key) { var cert64 = Convert.ToBase64String(x509Key.Certificate.RawData); var thumbprint = Base64Url.Encode(x509Key.Certificate.GetCertHash()); var pubKey = x509Key.PublicKey as RSA; var parameters = pubKey.ExportParameters(false); var exponent = Base64Url.Encode(parameters.Exponent); var modulus = Base64Url.Encode(parameters.Modulus); var webKey = new Models.JsonWebKey { kty = "RSA", use = "sig", kid = x509Key.KeyId, x5t = thumbprint, e = exponent, n = modulus, x5c = new[] { cert64 }, alg = algorithm }; webKeys.Add(webKey); continue; } if (key is RsaSecurityKey rsaKey) { var parameters = rsaKey.Rsa?.ExportParameters(false) ?? rsaKey.Parameters; var exponent = Base64Url.Encode(parameters.Exponent); var modulus = Base64Url.Encode(parameters.Modulus); var webKey = new Models.JsonWebKey { kty = "RSA", use = "sig", kid = rsaKey.KeyId, e = exponent, n = modulus, alg = algorithm }; webKeys.Add(webKey); } } return webKeys; }
关与 Token 签名与验签 https://jwt.io 中能够找到不一样语言的实现。
当使用 AccessTokenType 类型为 Jwt Token 时候,就会使用 Jwt 规范来生成 Token,签名的算法是采用 RSA (SHA256 签名) ,在服务端 IdentityServer4 使用私钥对 Token 进行签名,当客户端去资源端获取资源的时候,API 端(资源服务器)收到第一个请求后去服务端得到公钥而后验签(调用 /.well-known/openid-configuration/jwks 获取公钥这个过程只发生在客户端第一次请求,因此当服务端更换证书,资源端也须要重启服务)。通常开发环境使用 AddDeveloperSigningCredential 方法使用临时证书便可(先判断 tempkey.rsa 文件是否存在,若是不存在就建立一个新的文件 )。
/// <summary> /// Sets the temporary signing credential. /// </summary> /// <param name="builder">The builder.</param> /// <param name="persistKey">Specifies if the temporary key should be persisted to disk.</param> /// <param name="filename">The filename.</param> /// <returns></returns> public static IIdentityServerBuilder AddDeveloperSigningCredential(this IIdentityServerBuilder builder, bool persistKey = true, string filename = null) { if (filename == null) { filename = Path.Combine(Directory.GetCurrentDirectory(), "tempkey.rsa"); } if (File.Exists(filename)) { var keyFile = File.ReadAllText(filename); var tempKey = JsonConvert.DeserializeObject<TemporaryRsaKey>(keyFile, new JsonSerializerSettings { ContractResolver = new RsaKeyContractResolver() }); return builder.AddSigningCredential(CreateRsaSecurityKey(tempKey.Parameters, tempKey.KeyId)); } else { var key = CreateRsaSecurityKey(); RSAParameters parameters; if (key.Rsa != null) parameters = key.Rsa.ExportParameters(includePrivateParameters: true); else parameters = key.Parameters; var tempKey = new TemporaryRsaKey { Parameters = parameters, KeyId = key.KeyId }; if (persistKey) { File.WriteAllText(filename, JsonConvert.SerializeObject(tempKey, new JsonSerializerSettings { ContractResolver = new RsaKeyContractResolver() })); } return builder.AddSigningCredential(key); } } /// <summary> /// Creates a new RSA security key. /// </summary> /// <returns></returns> public static RsaSecurityKey CreateRsaSecurityKey() { var rsa = RSA.Create(); RsaSecurityKey key; if (rsa is RSACryptoServiceProvider) { rsa.Dispose(); var cng = new RSACng(2048); var parameters = cng.ExportParameters(includePrivateParameters: true); key = new RsaSecurityKey(parameters); } else { rsa.KeySize = 2048; key = new RsaSecurityKey(rsa); } key.KeyId = CryptoRandom.CreateUniqueId(16); return key; }
建立自签名证书
生成环境(负载集群)通常须要使用固定的证书签名与验签,以确保重启服务端或负载的时候 Token 都能验签经过。
数字证书常见标准
符合PKI ITU-T X509 标准,传统标准(.DER .PEM .CER .CRT)
符合PKCS#7 加密消息语法标准(.P7B .P7C .SPC .P7R)
符合PKCS#10 证书请求标准(.p10)
符合PKCS#12 我的信息交换标准(.pfx *.p12)X509是数字证书的基本规范,而P7和P12则是两个实现规范,P7用于数字信封,P12则是带有私钥的证书实现规范。
X.509
X.509 是数字证书一个标准,由用户公共密钥和用户标识符组成。此外还包括版本号、证书序列号、CA标识符、签名算法标识、签发者名称、证书有效期等信息。
PKCS#12
一种文件打包格式,为存储和发布用户和服务器私钥、公钥和证书指定了一个可移植的格式,是一种二进制格式,一般以.pfx或.p12为文件后缀名。使用OpenSSL的pkcs12命令能够建立、解析和读取这些文件。P12是把证书压成一个文件 *.pfx 。主要是考虑分发证书,私钥是要绝对保密的,不能随便以文本方式散播。因此P7格式不适合分发。.pfx中能够加密码保护,因此相对安全些。
能够在 Linux 上经过 OpenSSL 相关的命令生成数字证书
sudo apt-get install openssl
#生成私钥文件 openssl genrsa -out idsrv4.key 2048 #建立证书签名请求文件 CSR(Certificate Signing Request),用于提交给证书颁发机构(即 Certification Authority (CA))即对证书签名,申请一个数字证书。 openssl req -new -key idsrv4.key -out idsrv4.csr #生成自签名证书(证书颁发机构(CA)签名后的证书,由于本身作测试那么证书的申请机构和颁发机构都是本身,crt 证书包含持有人的信息,持有人的公钥,以及签署者的签名等信息。当用户安装了证书以后,便意味着信任了这份证书,同时拥有了其中的公钥。) openssl x509 -req -days 365 -in idsrv4.csr -signkey idsrv4.key -out idsrv4.crt #自签名证书与私匙合并成一个文件 openssl pkcs12 -export -in idsrv4.crt -inkey idsrv4.key -out idsrv4.pfx 或 openssl req -newkey rsa:2048 -nodes -keyout idsrv4.key -x509 -days 365 -out idsrv4.cer openssl pkcs12 -export -in idsrv4.cer -inkey idsrv4.key -out idsrv4.pfx
完成后会有三个文件(VS选中配置文件设置文件始终复制),最后把证书路径和密码配置到 IdentityServer 中,由于咱们自签名的证书是 PKCS12 (我的数字证书标准,Public Key Cryptography Standards #12) 标准包含私钥与公钥)标准,包含了公钥和私钥。
root@iZuf60cj5pna5im3va46nlZ:~# tree
.
├── idsrv4.cer
├── idsrv4.key
└── idsrv4.pfx
使用 IdentityModel.Tokens.Jwt 测试签名与验签
public static async Task Run() { try { //https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/master/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs //得到证书文件 var filePath = Path.Combine(AppContext.BaseDirectory, "Certs\\idsrv4.pfx"); if (!File.Exists(filePath)) { throw new FileNotFoundException("Signing Certificate is missing!"); } var credential = new SigningCredentials(new X509SecurityKey(new X509Certificate2(filePath, "123456")), "RS256"); if (credential == null) { throw new InvalidOperationException("No signing credential is configured. Can't create JWT token"); } var header = new JwtHeader(credential); // emit x5t claim for backwards compatibility with v4 of MS JWT library if (credential.Key is X509SecurityKey x509key) { var cert = x509key.Certificate; var pub_key = cert.GetPublicKeyString(); header["x5t"] = Base64Url.Encode(cert.GetCertHash()); } var payload = new JwtPayload(); payload.AddClaims(ClaimSets.DefaultClaims); var jwtTokenHandler = new JwtSecurityTokenHandler(); var jwtToken = jwtTokenHandler.WriteToken(new JwtSecurityToken(header, payload)); SecurityToken validatedSecurityToken = null; //ValidateToken var vaild = jwtTokenHandler.ValidateToken(jwtToken, new TokenValidationParameters { IssuerSigningKey = credential.Key, RequireExpirationTime = false, RequireSignedTokens = true, ValidateAudience = false, ValidateIssuer = false, ValidateLifetime = false, }, out validatedSecurityToken); //ReadJwtToken var readJwtToken = jwtTokenHandler.ReadJwtToken(jwtToken); } catch (Exception ex) { } }
IdentityServer4 服务端修改代码
//得到证书文件 var filePath = Path.Combine(AppContext.BaseDirectory, Configuration["Certs:Path"]); if (!File.Exists(filePath)) { throw new FileNotFoundException("Signing Certificate is missing!"); } var x509Cert = new X509Certificate2(filePath, Configuration["Certs:Pwd"]); var credential = new SigningCredentials(new X509SecurityKey(x509Cert), "RS256"); // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer(options => { options.Events.RaiseErrorEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseSuccessEvents = true; }) //.AddDeveloperSigningCredential() //.AddDeveloperSigningCredential(persistKey: true, filename: "rsakey.rsa")、 .AddSigningCredential(x509Cert) //.AddSigningCredential(credential) .AddInMemoryApiResources(InMemoryConfig.GetApiResources()) .AddInMemoryIdentityResources(InMemoryConfig.GetIdentityResources()) .AddInMemoryClients(InMemoryConfig.GetClients()) .AddTestUsers(InMemoryConfig.GetUsers().ToList()); //.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>() //.AddProfileService<ProfileService>();
运行访问 /.well-known/openid-configuration/jwks 查询公钥的信息(Jwks Endpoint)
GET http://localhost:5000/.well-known/openid-configuration HTTP/1.1 Host: localhost:5000 HTTP/1.1 200 OK Content-Type: application/json; charset=UTF-8 Server: Kestrel X-SourceFiles: =?UTF-8?B?RDpcZ2l0aHViXFNlY3VyaW5nRm9yV2ViQVBJXElkU3J2NC5Ib3N0U3J2XC53ZWxsLWtub3duXG9wZW5pZC1jb25maWd1cmF0aW9u?= X-Powered-By: ASP.NET Date: Tue, 24 Jul 2018 12:43:48 GMT Content-Length: 1313 {"issuer":"http://localhost:5000","jwks_uri":"http://localhost:5000/.well-known/openid-configuration/jwks","authorization_endpoint":"http://localhost:5000/connect/authorize","token_endpoint":"http://localhost:5000/connect/token","userinfo_endpoint":"http://localhost:5000/connect/userinfo","end_session_endpoint":"http://localhost:5000/connect/endsession","check_session_iframe":"http://localhost:5000/connect/checksession","revocation_endpoint":"http://localhost:5000/connect/revocation","introspection_endpoint":"http://localhost:5000/connect/introspect","frontchannel_logout_supported":true,"frontchannel_logout_session_supported":true,"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"scopes_supported":["api","user","order","offline_access"],"claims_supported":[],"grant_types_supported":["authorization_code","client_credentials","refresh_token","implicit","password"],"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"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"code_challenge_methods_supported":["plain","S256"]} GET http://localhost:5000/.well-known/openid-configuration/jwks HTTP/1.1 Host: localhost:5000 HTTP/1.1 200 OK Content-Type: application/json; charset=UTF-8 Server: Kestrel X-SourceFiles: =?UTF-8?B?RDpcZ2l0aHViXFNlY3VyaW5nRm9yV2ViQVBJXElkU3J2NC5Ib3N0U3J2XC53ZWxsLWtub3duXG9wZW5pZC1jb25maWd1cmF0aW9uXGp3a3M=?= X-Powered-By: ASP.NET Date: Tue, 24 Jul 2018 12:43:48 GMT Content-Length: 451 {"keys":[{"kty":"RSA","use":"sig","kid":"0fdf841efb8c990ea6f2b09318c0cba2","e":"AQAB","n":"zDMobgJ8pjUAH_e8EqtYZE-t14InmDDcpDqdQp9bT0bGiOpvLpgqgsFJulAwKQfhPwwOwUBKq7Lle461Gb1PRug4L1zN3U-WA9cj0LL4dAHqGCXEazl3FTvWGe8FrQQRTgi8q-I2X_Jhxp8BYQkfatFknVUZSDYudxL-fIDJOSVYus-oEfhupQf_b1Le27UvfMuswVsUhKHbL2wSy_ZtdbY1X8pJ5XoLJwL2AO62Ahfb8ptHBI_Nbc285hAuB4WTPVcIdpp99Oodf6wTiflTVWLGqWP3o48VlxNyixUJCWqWI78BTno06U9cISBTAwbXFLADqjJDYz4OZOAn7Np_DQ","alg":"RS256"}]}
在 IdentityServer4 中当使用 Self-contained Json Web Token (自包含无状态的 Jwt Token)的时候,生成的Token 即为 Jwt 标准格式(Token 包含了三部分: Header 头部 Payload 负载 Signature 签名,使用.分隔的)格式,在资源端(API)就能够完成验签的过程,不须要每次再去资源端验签以减小网络请求,缺点就是生成的 Token 会很长,另外 Token 是不可撤销的,Token 的生命周期(被验证经过)会一直到票据过时,若是泄露就会比较麻烦。
当使用 Reference token 的时候,服务端会对 Token 进行持久化,当客户端请求资源端(API)的时候,资源端须要每次都去服务端通讯去验证 Token 的合法性[/connect/introspect],IdentityServer4.AccessTokenValidation 中间件中能够配置缓存必定的时候去验证,而且 Token 是支持撤销[/connect/revocation]的。
上述涉及到的接口:
Revocation 与 Introspection 都属于 OAuth2.0 协议的的标准规范,另外要使用 Introspection 接口的时候, IdentityServer4 中 ApiResource 中需定义 ApiSecrets(资源端去服务端验证须要相应的参数)。
var api = new ApiResource("api") { ApiSecrets = { new Secret("secret".Sha256()) } }
Token 验证
API 端(资源服务器)须要每次去访问 IdentityServer4 服务端来验证 Token 的合法性(POST /connect/introspect),固然 API 端也能够配置必定的时间来缓存结果,以减小通讯的频率。
POST http://localhost:5000/connect/introspect HTTP/1.1 Accept: application/json Content-Type: application/x-www-form-urlencoded Content-Length: 135 Host: localhost:5000 token=c92ef5a5bbb8333dde392a4aa1e0bba6aa774bc7441d5f71d01ebca1a71f07e5&client_id=api&token_type_hint=access_token&client_secret=api_pwd HTTP/1.1 200 OK Cache-Control: no-store, no-cache, max-age=0 Pragma: no-cache Content-Type: application/json; charset=UTF-8 Server: Kestrel X-SourceFiles: =?UTF-8?B?RDpcZ2l0aHViXFNlY3VyaW5nRm9yV2ViQVBJXElkU3J2NC5Ib3N0U3J2XGNvbm5lY3RcaW50cm9zcGVjdA==?= X-Powered-By: ASP.NET Date: Wed, 25 Jul 2018 10:17:19 GMT Content-Length: 164 {"iss":"http://localhost:5000","nbf":1532513838,"exp":1532517438,"aud":["http://localhost:5000/resources","api"],"client_id":"client_2","active":true,"scope":"api"}
.AddIdentityServerAuthentication(options => { // base-address of your identityserver options.Authority = "https://demo.identityserver.io"; //name of the API resource options.ApiName = "api1"; options.ApiSecret = "secret"; options.EnableCaching = true; options.CacheDuration = TimeSpan.FromMinutes(10); //that's the default })
备注:Access token validation middleware
.Net 中 Jwt token 与 Reference token 相应的中间件也不同(Microsoft.AspNetCore.Authentication.JwtBearer,IdentityModel.AspNetCore.OAuth2Introspection ),为了方便官方只是把二者集成到了一块儿(IdentityServer4.AccessTokenValidation),只要符合协议规范,其余语言也有相应的集成方式 。
REFER:
https://identityserver4.readthedocs.io/en/release/topics/reference_tokens.html
https://identityserver4.readthedocs.io/en/release/topics/crypto.html#refcrypto
https://blogs.msdn.microsoft.com/webdev/2016/10/27/bearer-token-authentication-in-asp-net-core/
https://www.cnblogs.com/edisonchou/p/identityserver4_foundation_and_quickstart_01.html
用 Identity Server 4 (JWKS 端点和 RS256 算法) 来保护 Python web api
http://www.javashuo.com/article/p-dzupnjqd-c.html
数字证书原理
http://www.cnblogs.com/JeffreySun/archive/2010/06/24/1627247.html
http://www.javashuo.com/article/p-ytojebef-o.html
http://www.javashuo.com/article/p-gwfgcrxz-bq.htmlhttps://auth0.com/blog/navigating-rs256-and-jwks/
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html