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