api-gateway實踐(10)新服務網關 - OpenID Connect


網關指南: https://help.aliyun.com/document_detail/29487.html?spm=5176.doc48835.6.550.23Oqbl

網關控制台: https://apigateway.console.aliyun.com/?spm=5176.doc42740.2.2.Q4z5ws#/cn-hangzhou/apis/list

參考鏈接:https://help.aliyun.com/document_detail/48019.html?spm=5176.doc29487.6.580.NFBkpz

參考鏈接:https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples

JSON Web Token (JWT) Code Examples

 

一、配置中心

1、配置API時,將所有API分為兩類,【獲得授權Token的API】和【業務訪問API】。

2、【獲得授權Token的API】用於客戶端獲得token。配置【獲得授權Token的API】時,要指定Token對應的KeyId和解析Token的公鑰。

3、【業務訪問API】實際業務API,調用時需攜帶token。配置【業務訪問API】時,要指定請求Token名稱。

當客戶端調用這類API的請求到達API網關后,API網關自動驗證這個請求的Appkey和Token是否合法。

 

3.1、系統需要為【業務訪問API】增加如下請求query參數:

3.2、系統自動向后方傳遞解析后的用戶參數(claims)

       參數位置支持:不要往body里放,query、head,默認head。

二、業務流程

1、客戶端調用 “獲取授權 API” ,獲取Token的流程

1.1、客戶端使用您的 “Appkey 簽名”+“用戶名/密碼” 調用 “獲取授權” API 獲取(加密后的)Token

用戶名/密碼 是極為敏感的信息,在網絡中明文傳輸存在風險,建議在傳輸前對用戶名密碼再次加密,並使用 HTTPS 協議傳輸。

1.2、API網關收到請求后,先認證您的 Appkey(簽名比對?),認證通過后,調用后端服務的賬號系統認證您傳遞的 “用戶名/密碼”。

1.3、后端服務認證通過后,(后端服務)依據網關提供的加密方式,頒發 Token返回給客戶端,后續客戶端可憑 Token 來調用 “業務 API”。

2、客戶端調用業務類 API ,來實現業務功能

2.1、客戶端(簽名比對)使用 “獲取授權 API” 得到的 Token 來調用 “業務API”。

2.2、API 網關認證、解析 Token 的內容(claims),並將 Token 中包含的用戶信息(claims)傳遞給后端。 (ca_x_openid.xxxx -> xxxxx,ca_x_openid.yyyy -> yyyy

       參數位置支持:不要往body里放,query、head,默認head。

三、認證服務器AS(服務提供者實現)

認證服務器的職責

  • 認證服務器負責生成 id_Token 並管理公鑰私鑰對
  • 認證服務器接收網關請求(u+p),執行u+p認證。
    • 認證成功:返回token(包含了用戶信息)
    • 認證失敗:返回出錯信息 注:id_token 必須符合 OIDC(1.0版本)協議中的 規范。

id_token:id_token 是在 OIDC 協議中定義的一種令牌,詳細內容參見 OpenID Connect Core 1.0。

id_token 的生成需要 KeyPair, keyId 與 Claims (有關Claims更多信息請訪問 ID_Token)。

1、KeyId

KeyId 必須保證唯一, 比如使用 UUID 生成的長度至少32位的隨機字符串, 可以全為數字或數字+字母。

參考示例(JAVA)

String keyId = UUID.randomUUID().toString().replaceAll("-", "");

String keyId = String.valueOf(UUID.randomUUID().getMostSignificantBits()) + String.valueOf(UUID.randomUUID().getMostSignificantBits());

2、KeyPair

KeyPair 是一個基於 PKI 體系的非對稱算法的公私鑰組合, 每一對包括公鑰(publicKey)與私鑰(privateKey);

公鑰放置在 RS 中,在校驗(verify)時使用, 私鑰放置在 AS 中,在生成 id_token 時做數字簽名使用;

KeyPair 使用 RSA SHA256 加密算法, 為保證足夠安全其加密的位數為2048;

AS 中使用的 KeyPair 均為 JSON 格式的數據,示例如下:

publicKey:

{"kty":"RSA","kid":"67174182967979709913950471789226181721","alg":"ES256","n":"oH5WunqaqIopfOFBz9RfBVVIIcmk0WDJagAcROKFiLJScQ8N\_nrexgbCMlu-dSCUWq7XMnp1ZSqw-XBS2-XEy4W4l2Q7rx3qDWY0cP8pY83hqxTZ6-8GErJm\_0yOzR4WO4plIVVWt96-mxn3ZgK8kmaeotkS0zS0pYMb4EEOxFFnGFqjCThuO2pimF0imxiEWw5WCdREz1v8RW72WdEfLpTLJEOpP1FsFyG3OIDbTYOqowD1YQEf5Nk2TqN\_7pYrGRKsK3BPpw4s9aXHbGrpwsCRwYbKYbmeJst8MQ4AgcorE3NPmp-E6RxA5jLQ4axXrwC0T458LIVhypWhDqejUw","e":"AQAB"}

 

privateKey:

{"kty":"RSA","kid":"67174182967979709913950471789226181721","alg":"ES256","n":"oH5WunqaqIopfOFBz9RfBVVIIcmk0WDJagAcROKFiLJScQ8N\_nrexgbCMlu-dSCUWq7XMnp1ZSqw-XBS2-XEy4W4l2Q7rx3qDWY0cP8pY83hqxTZ6-8GErJm\_0yOzR4WO4plIVVWt96-mxn3ZgK8kmaeotkS0zS0pYMb4EEOxFFnGFqjCThuO2pimF0imxiEWw5WCdREz1v8RW72WdEfLpTLJEOpP1FsFyG3OIDbTYOqowD1YQEf5Nk2TqN\_7pYrGRKsK3BPpw4s9aXHbGrpwsCRwYbKYbmeJst8MQ4AgcorE3NPmp-E6RxA5jLQ4axXrwC0T458LIVhypWhDqejUw","e":"AQAB","d":"aQsHnLnOK-1xxghw2KP5JTZyJZsiwt-ENFqqJfPUzmlYSCNAV4T39chKpkch2utd7hRtSN6Zo4NTnY8EzGQQb9yvunaiEbWUkPyJ6kM3RdlkkGLvVtp0sRwPCZ2EAYBlsMad9jkyrtmdC0rtf9jerzt3LMLC7XWbnpC3WAl8rsRDR1CGs\_-u4sfZfttsaUbJDD9hD0q4NfLDCVOZoQ\_8wkZxyWDAQGCe6GcCbu6N81fTp2CSVbiBj7DST\_4x2NYUA2KG8vyZYcwviNTxQzk4iPfdN2YQz\_9aMTZmmhVUGlmTvAjE5ebBqcqKAS0NfhOQHg2uR46eBKBy\_OyVOLohsQ","p":"8Tdo3DCs-0t9JMtM0lYqPRP4wYJs37Rv6S-ygRui2MI\_hadTY9I2A199JMYw7Fjke\_wa3gqJLa98pbybdLWkrOxXbKEkwE4uc4-fuNjLbUTC5tqdM5-nXmpL887uREVYnk8FUzvWeXYTCNCb7OLw5l8yPJ1tR8aNcd0fJNDKh98","q":"qlRrGSTsZzBkDgDi1xlCoYvoM76cbmxrCUK-mc\_kBRHfMjlHosxFUnAbxqIBE4eAJEKVfIJLQrHFvIDjQb3kM9ylmwMCu9f8u9DHrT8J7LSDlLqDaXuiM2oiKtW3bAaBPuiR7sVMFcuB5baCebHU487YymJCBTfeCZtFdi6c4w0","dp":"gVCROKonsjiQCG-s6X4j-saAL016jJsw-7QEYE6uiMHqR\_6iJ\_uD1V8Vuec-RxaItyc6SBsh24oeqsNoG7Ndaw7w912UVDwVjwJKQFCJDjU0v4oniItosKcPvM8M0TDUB1qZojuMCWWRYsJjNSWcvAQA7JoBAd-h6I8AqT39tcU","dq":"BckMQjRg2zhnjZo2Gjw\_aSFJZ8iHo7CHCi98LdlD03BB9oC\_kCYEDMLGDr8d7j3h-llQnoQGbmN\_ZeGy1l7Oy3wpG9TEWQEDEpYK0jWb7rBK79hN8l1CqyBlvLK5oi-uYCaiHkwRQ4RACz9huyRxKLOz5VvlBixZnFXrzBHVPlk","qi":"M5NCVjSegf\_KP8kQLAudXUZi\_6X8T-owtsG\_gB9xYVGnCsbHW8gccRocOY1Xa0KMotTWJl1AskCu-TZhOJmrdeGpvkdulwmbIcnjA\_Fgflp4lAj4TCWmtRI6982hnC3XP2e-nf\_z2XsPNiuOactY7W042D\_cajyyX\_tBEJaGOXM"} 示例代碼(JAVA) String keyId = UUID.randomUUID().toString().replaceAll("-", ""); RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk(2048); jwk.setKeyId(keyId); jwk.setAlgorithm(AlgorithmIdentifiers.ECDSA\_USING\_P256\_CURVE\_AND\_SHA256); String publicKey = jwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC\_ONLY); String privateKey = jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE\_PRIVATE); (3) Claims 通過 OIDC 協議中定義的 Claims 屬性(aud, sub, exp, iat, iss)與其屬性值,生成 Claims(全稱 JwtClaims) 示例代碼(JAVA) JwtClaims claims = new JwtClaims(); claims.setGeneratedJwtId(); claims.setIssuedAtToNow(); //expire time NumericDate date = NumericDate.now(); date.addSeconds(120); claims.setExpirationTime(date); claims.setNotBeforeMinutesInThePast(1); claims.setSubject("YOUR_SUBJECT"); claims.setAudience("YOUR_AUDIENCE"); //添加自定義參數 claims.setClaim(key, value); (4) 通過 keyId, Claims, privateKey 與使用的數字簽名算法 (RSA SHA256 )生成 JWS( Json Web Signature) 示例代碼(JAVA) JsonWebSignature jws = new JsonWebSignature(); jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA\_USING\_SHA256); jws.setKeyIdHeaderValue(keyId); ws.setPayload(claims.toJson()); PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyText)).getPrivateKey(); jws.setKey(privateKey); (5)通過 JWS 獲取 id_token 值 示例代碼(JAVA) String idToken = jws.getCompactSerialization(); 一個生成的 id_token 示例: eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg4NDgzNzI3NTU2OTI5MzI2NzAzMzA5OTA0MzUxMTg1ODE1NDg5In0.eyJ1c2VySWQiOiIzMzcwMTU0NDA2ODI1OTY4NjI3IiwidGFnTmFtZSI6ImNvbmFuVGVzdCIsImV4cCI6MTQ4MDU5Njg3OSwiYXVkIjoiQWxpX0FQSV9Vc2VyIiwianRpIjoiTm9DMFVVeW5xV0N0RUFEVjNoeEIydyIsImlhdCI6MTQ4MDU5MzI3OSwibmJmIjoxNDgwNTkzMjE5LCJzdWIiOiJ7ZGF0YU1hcD0ne3VzZXJJZD0zMzcwMTU0NDA2ODI1OTY4NjI3fScsIHN0YXR1c0NvZGU9JzAnLCBlcnJvcnM9J1tdJ30ifQ.V3rU2VCziSt6uTgdCktYRsIwkMEMsO_jUHNCCIW_Sp4qQ5ExjtwNt9h9mTGKFRujk2z1E0k36smWf9PbNGTZTWmSYN8rvcQqdsupcC6LU9r8jreA1Rw1CmmeWY4HsfBfeInr1wCFrEfZl6_QOtf3raKSK9AowhzEsnYRKAYuc297gmV8qlQdevAwU75qtg8j8ii3hZpJqTX67EteNCHZfhXn8wJjckl5sHz2xPPyMqj8CGRQ1wrZEHjUmNPw-unrUkt6neM0UrSqcjlrQ25L8PEL2TNs7nGVdl6iS7Nasbj8fsERMKcZbP2RFzOZfKJuaivD306cJIpQwxfS1u2bew

示例代碼(JAVA)
String keyId = UUID.randomUUID().toString().replaceAll("-", "");
RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk(2048);
jwk.setKeyId(keyId);
jwk.setAlgorithm(AlgorithmIdentifiers.ECDSA\_USING\_P256\_CURVE\_AND\_SHA256);
String publicKey = jwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC\_ONLY);
String privateKey = jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE\_PRIVATE);

3、id_token的生成

3.1、構建 Claims

通過 OIDC 協議中定義的 Claims 屬性(aud, sub, exp, iat, iss)與其屬性值,生成 Claims(全稱 JwtClaims)

示例代碼(JAVA)

JwtClaims claims = new JwtClaims();
claims.setGeneratedJwtId();
claims.setIssuedAtToNow();
//expire time
NumericDate date = NumericDate.now();
date.addSeconds(120);
claims.setExpirationTime(date);
claims.setNotBeforeMinutesInThePast(1);
claims.setSubject("YOUR_SUBJECT");
claims.setAudience("YOUR_AUDIENCE");
//添加自定義參數
claims.setClaim(key, value);

3.2、構建jws

通過 keyId, Claims, privateKey 與使用的數字簽名算法 (RSA SHA256 )生成 JWS( Json Web Signature)

示例代碼(JAVA)

JsonWebSignature jws = new JsonWebSignature();
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA\_USING\_SHA256);
jws.setKeyIdHeaderValue(keyId);
ws.setPayload(claims.toJson());
PrivateKey privateKey = newRsaJsonWebKey(JsonUtil.parseJson(privateKeyText)).getPrivateKey();
jws.setKey(privateKey);

3.3、通過 JWS 獲取 id_token 值

示例代碼(JAVA)

String idToken = jws.getCompactSerialization();
一個生成的 id_token 示例:

eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg4NDgzNzI3NTU2OTI5MzI2NzAzMzA5OTA0MzUxMTg1ODE1NDg5In0.eyJ1c2VySWQiOiIzMzcwMTU0NDA2ODI1OTY4NjI3IiwidGFnTmFtZSI6ImNvbmFuVGVzdCIsImV4cCI6MTQ4MDU5Njg3OSwiYXVkIjoiQWxpX0FQSV9Vc2VyIiwianRpIjoiTm9DMFVVeW5xV0N0RUFEVjNoeEIydyIsImlhdCI6MTQ4MDU5MzI3OSwibmJmIjoxNDgwNTkzMjE5LCJzdWIiOiJ7ZGF0YU1hcD0ne3VzZXJJZD0zMzcwMTU0NDA2ODI1OTY4NjI3fScsIHN0YXR1c0NvZGU9JzAnLCBlcnJvcnM9J1tdJ30ifQ.V3rU2VCziSt6uTgdCktYRsIwkMEMsO_jUHNCCIW_Sp4qQ5ExjtwNt9h9mTGKFRujk2z1E0k36smWf9PbNGTZTWmSYN8rvcQqdsupcC6LU9r8jreA1Rw1CmmeWY4HsfBfeInr1wCFrEfZl6_QOtf3raKSK9AowhzEsnYRKAYuc297gmV8qlQdevAwU75qtg8j8ii3hZpJqTX67EteNCHZfhXn8wJjckl5sHz2xPPyMqj8CGRQ1wrZEHjUmNPw-unrUkt6neM0UrSqcjlrQ25L8PEL2TNs7nGVdl6iS7Nasbj8fsERMKcZbP2RFzOZfKJuaivD306cJIpQwxfS1u2bew

 

四、資源服務器RS

資源服務器RS

  • 資源服務器RS負責校驗 id_token,
  • 並解析出相應的信息 Consumer 用帶有 id_token 的參數去請求 API 網關。

API 網關保存了校驗所使用的公鑰,驗證並解析 id_token 獲取其中的 User 信息傳給 Provider(ca_x_openid.xxxx->xxxx),若驗證失敗則直接返回錯誤信息。

Provider 處理請求並返回結果給 API 網關。 API 網關透傳 Provider 響應的結果給 Consumer。

說明:RS 在整個體系中擔任 id_token 消費者角色,只有 id_token 校驗通過,才能將請求轉發給 Provider。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM