本篇文檔以 Java 為例,講解作為應用與 IDaaS 的對接。
若您希望瞭解對接原理和調用流程,請參考 賬戶同步接入概述。
接入賬戶同步可能需要處理兩點:
驗簽
解密(可選)
進行完上述過程後,即可擷取到該次事件的請求內容,應用自行處理即可。
1. 驗簽
參考 賬戶同步 - IDaaS 同步到應用 中操作,從應用同步配置中擷取公開金鑰端點。
在本文檔樣本中,提供了直接從公開金鑰端點擷取公開金鑰的 Java 工具類。若您所選開發語言需要公開金鑰資訊,可能需要您點開公開金鑰端點,擷取公開金鑰內容,轉化成 .pem 檔案格式儲存在本地。
公開金鑰樣本:
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "KEY3PdQDx97********h83p8husNSC9AKMH",
"n": "rLUnH5PNeGUZE-********GGIxyM5O7TDdaG4********D9mV1CjE8hVHBxXM96IcCCH_1xmUZEZRp_MBP6m2XeNWUXanCpeyuIAD2kxmaQAqituZdIlT4l3-q9gtccdY-khaE-OfH9qYZhlxFcYj0gVtOvKZFIkuGhME4IQJd_RAWS3OPXxtbGhO2fZYCiuuc8NWub5mcVQnqsy5aJPLwHbVwVUwYNOmaq97_m2TtPcIVWtw7AOzX8O78UrYnYt_QPrv7uVdJMbHleSOx2A1IXqrAkJWecwFfvTsBTCUOPPDeVRQEHzzwmf3zpz5KMgHZU1I5pyqi0KJ6BuMHWw"
}
]
}
添加 Maven 依賴:
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.7.9</version>
</dependency>
工具類代碼如下,用於從 IDaaS 公開金鑰端點擷取公開金鑰並驗簽,您可以直接複製使用:
import org.apache.commons.codec.binary.StringUtils;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class JwtUtil {
private final static ConcurrentMap<String, JsonWebKeySet> IDAAS_SIGN_JWK_SET_MAP = new ConcurrentHashMap<>();
public static JwtConsumer createJwtConsumerFromUrl(String jwkUrl, String appId) {
try {
final JsonWebKeySet jsonWebKeySet = getJsonWebKeySetByUrl(jwkUrl);
return createJwtConsumer(jsonWebKeySet, appId);
} catch (Exception e) {
throw new RuntimeException("Fetch JWKs from url failed: " + e.getMessage() + ", " + jwkUrl, e);
}
}
public static JwtConsumer createJwtConsumer(JsonWebKeySet jsonWebKeySet, String appId) {
final JwtConsumerBuilder jwtConsumerBuilder = new JwtConsumerBuilder();
jwtConsumerBuilder.setExpectedIssuer("urn:alibaba:idaas:app:event");
jwtConsumerBuilder.setRequireExpirationTime();
jwtConsumerBuilder.setRequireJwtId();
jwtConsumerBuilder.setRequireIssuedAt();
jwtConsumerBuilder.setRequireExpirationTime();
jwtConsumerBuilder.setMaxFutureValidityInMinutes(1);
jwtConsumerBuilder.setAllowedClockSkewInSeconds(120);
jwtConsumerBuilder.setExpectedAudience(appId);
jwtConsumerBuilder.setVerificationKeyResolver((jws, nestingContext) -> {
final String signKeyId = jws.getKeyIdHeaderValue();
for (JsonWebKey jsonWebKey : jsonWebKeySet.getJsonWebKeys()) {
if (StringUtils.equals(jsonWebKey.getKeyId(), signKeyId)) {
return jsonWebKey.getKey();
}
}
throw new RuntimeException("Cannot find verification key: " + signKeyId);
});
return jwtConsumerBuilder.build();
}
synchronized private static JsonWebKeySet getJsonWebKeySetByUrl(String jwkUrlString) throws IOException, JoseException {
JsonWebKeySet jsonWebKeySet = IDAAS_SIGN_JWK_SET_MAP.get(jwkUrlString);
if (jsonWebKeySet == null) {
jsonWebKeySet = innerGetJsonWebKeySetByUrl(jwkUrlString);
IDAAS_SIGN_JWK_SET_MAP.put(jwkUrlString, jsonWebKeySet);
}
return jsonWebKeySet;
}
private static JsonWebKeySet innerGetJsonWebKeySetByUrl(String jwkUrlString) throws IOException, JoseException {
final URL jwkUrl = new URL(jwkUrlString);
final URLConnection urlConnection = jwkUrl.openConnection();
urlConnection.setConnectTimeout(50000);
urlConnection.setReadTimeout(50000);
final String jwkSetJson = new String(readAll(urlConnection.getInputStream()), StandardCharsets.UTF_8);
return new JsonWebKeySet(jwkSetJson);
}
public static byte[] readAll(InputStream inputStream) throws IOException {
final byte[] buffer = new byte[1024 * 8];
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int len; ((len = inputStream.read(buffer)) != -1); ) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
}
}
調用範例程式碼:
//公開金鑰->IDaaS應用同步配置裡,訪問應用公開金鑰端點後擷取到。
String publicKey = "{\n"
+ " \"keys\": [\n"
+ " {\n"
+ " \"kty\": \"RSA\",\n"
+ " \"e\": \"AQAB\",\n"
+ " \"use\": \"sig\",\n"
+ " \"kid\": \"KEYHH4yFa1c*******qNo1nJ7nM2FR3595P1\",\n"
+ " \"n\": \"oy_xxxxxxxxxxxxxxxxxxxxxxx95d1padSEABqIbcTKcnlTaET3WHaR"
+ "-3MvsooeZWluv94GQEp-U2jzM1adgTqBl_7KPjUk0dwrZbob_8pOLX5UQMF7Oo_nH5-H5EyL9-yGGhFA4oeuA"
+ "-b73qXShxP7eHs5xTT1kiYEu2NE3rBZdtrRwUiC_h1DvZMtyWFOPwm3dpLiwCcdlgcKvVuSEXyCBj6Gjevn3_G1guVQ2kHlNOVyNn6Ky1iGQJzXctJCEJ5fnBRs4XZZbPNSciYMD2-__cRdbYPtGyyuoEAfouw\"\n"
+ " }\n"
+ " ]\n"
+ "}";;
//應用ID->應用列表中,找到對應的應用ID
String appId = "app_mjavzivahje6zxxxx";
//JwtUtil->下面已提供JwtUtil工具類
JwtConsumer jwtConsumer = JwtUtil.createJwtConsumer(new JsonWebKeySet(publicKey),appId);
//JWT驗簽後,擷取到payload
//event參數值->介面接收到的參數值
JwtClaims jwtClaims = jwtConsumer.processToClaims("event參數的值");
//擷取到具體的payload
Map<String, Object> map = jwtClaims.getClaimsMap();
//接下來,根據具體的資料,做對應的業務處理
2. 解密(可選)
資料解密
若應用開啟業務資料加密,事件數目據將通過 cipher_data 加密傳遞,業務方需要解密,以擷取到同步資料。
IDaaS 支援自主填寫加密金鑰,也可由 IDaaS 產生。
將密鑰複製出來,解密時使用。
新增 Maven 依賴:
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.7.9</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
解密範例程式碼:
public String decrypte(String cipherData,String key) throws JoseException {
String alg = "AES";
// 產生使用密鑰產生 KeySpec
SecretKeySpec secretKeySpec = new SecretKeySpec(Hex.decode(key), alg);
JsonWebKey jsonWebKey = JsonWebKey.Factory.newJwk(secretKeySpec);
JsonWebEncryption receiverJwe = new JsonWebEncryption();
// 設定加解密機制
AlgorithmConstraints algConstraints = new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, new String[]{"dir"});
receiverJwe.setAlgorithmConstraints(algConstraints);
AlgorithmConstraints encConstraints = new AlgorithmConstraints(
AlgorithmConstraints.ConstraintType.PERMIT, new String[]{"A256GCM", "A192GCM", "A128GCM"});
receiverJwe.setContentEncryptionAlgorithmConstraints(encConstraints);
// 傳入密鑰和密文
receiverJwe.setKey(jsonWebKey.getKey());
receiverJwe.setCompactSerialization(cipherData);
// 返回解密內容
return new String(receiverJwe.getPlaintextBytes(), StandardCharsets.UTF_8);
}