一、概述
通過訂閱用戶信息變更,您可以接收有關用戶及其賬戶的重要更新。當用戶取消元服務的授權信息、注銷華為賬號時,華為賬號服務器會發送通知到元服務,元服務可以根據通知消息進行自身業務處理。
二、用戶信息變更事件介紹
?
三、訂閱用戶信息變更
訂閱步驟如下:
1.登錄華為開發者聯盟,選擇“管理中心 > API服務 > API庫”。
2.在App Services找到RISC。
3.點擊啟用按鈕,選擇您的項目,點擊確定。
4.點擊訂閱通知按鈕,在彈窗中配置回調地址及訂閱范圍。
說明
回調地址:在開啟訂閱通知后,若華為用戶信息存在變更,會通過發送消息到該地址。
訂閱范圍:訂閱的用戶信息變更事件,詳見用戶信息變更事件介紹。
四、處理通知消息
華為賬號服務器向元服務投遞消息。元服務接收到消息后需要先對消息頭中的令牌進行驗簽,確保消息的完整有效性后解析并獲取用戶信息變更事件詳情。具體步驟如下:
1.驗證消息頭中的令牌簽名。
您可通過任何JWT庫(例如:jwt.io)對其進行解析與驗證。
無論使用哪種庫,您均需完成如下操作:
調用接口(https://risc.cloud.huawei.com/v1beta/public/risc/.well-known/risc-configuration),獲取發行者標識(issuer)與簽名密鑰證書URI(jwks_uri)。
通過依賴的JWT庫,對消息頭中的令牌進行解析,獲取簽名的KeyId。
通過簽名的KeyId,從簽名密鑰證書URI中獲取到JWT簽名的公鑰。
校驗JWT簽名中的aud與訂閱用戶信息變更中提供的Client ID一致。
校驗JWT簽名中的issuer與發行者標識(issuer)一致。
具體驗簽邏輯,請參考如下示例代碼:
Maven依賴配置
<dependencies><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.9.3</version> <!--此處替換為您項目需要的版本--></dependency><dependency> <groupId>com.auth0</groupId> <artifactId>jwks-rsa</artifactId><version>0.21.2</version> <!--此處替換為您項目需要的版本--></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version> <!--此處替換為您項目需要的版本--></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version> <!--此處替換為您項目需要的版本--></dependency>
</dependencies>
Java驗簽代碼示例
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.UrlJwkProvider;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.IncorrectClaimException;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SigningKeyResolver;
import io.jsonwebtoken.security.SignatureException;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Key;
import java.security.PublicKey;
import java.util.Objects;
import java.util.concurrent.TimeUnit;public class RiscDemo {public static void main(String[] args) {// 消息請求頭中Authorization: Bearer <token>中的<token>String token = "
<token>
";// Client IDString clientId = "
<Client ID>
";Jwt<?, Object> jwt = validateSecurityEventToken(token, clientId);if (Objects.isNull(jwt)) {// 驗簽失敗,進行自己邏輯處理} else {// 驗簽成功,進行自己邏輯處理}}/*** 對Authorization頭域中的token進行驗簽** @param token 消息請求頭中Authorization: Bearer <token>中的<token>* @param clientId Client ID** @return 返回為null,則表示驗簽失敗,否則表示驗證成功*/public static <H extends Header<H>, B> Jwt<H, B> validateSecurityEventToken(String token, String clientId) {Jwt<H, B> jwt = null;try {/*** 公開配置信息地址:https://risc.cloud.huawei.com/v1beta/public/risc/.well-known/risc-configuration* 公開配置信息中的issuer值*/String issuer = "id.cloud.huawei.com";// 公開配置信息中的jwks_uri值String jwksUri = "https://risc.cloud.huawei.com/v1beta/public/risc/certs";// 獲取公鑰信息JwkProvider huaweiCerts = new UrlJwkProvider(new URL(jwksUri), null, null);LoadingCache<String, PublicKey> cache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.DAYS).build(new CacheLoader<String, PublicKey>() {@Overridepublic @Nullable PublicKey load(@NonNull String key) throws Exception {return huaweiCerts.get(key).getPublicKey();}});SigningKeyResolver signingKeyResolver = new SigningKeyResolver() {private PublicKey getPublicKey(JwsHeader<?> jwsHeader) {try {return cache.get(jwsHeader.getKeyId());} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic Key resolveSigningKey(JwsHeader jwsHeader, Claims claims) {return getPublicKey(jwsHeader);}@Overridepublic Key resolveSigningKey(JwsHeader jwsHeader, String s) {return getPublicKey(jwsHeader);}};// 驗證并解析消息內容JwtParser parser = Jwts.parserBuilder().requireIssuer(issuer).requireAudience(clientId).setAllowedClockSkewSeconds(60).setSigningKeyResolver(signingKeyResolver).build();jwt = parser.parse(token);} catch (IncorrectClaimException e) {// 消息的claim無效,針對異常進行處理(如:日志記錄)e.printStackTrace();} catch (SignatureException e) {// 驗簽失敗,針對異常進行處理(如:日志記錄)e.printStackTrace();} catch (MalformedURLException e) {// 無效的jwksUri,檢查傳入的jwksUri是否與https://risc.cloud.huawei.com/v1beta/public/risc/.well-known/risc-configuration返回jwks_uri一致e.printStackTrace();} catch (Exception e) {// 其他異常,業務自行處理e.printStackTrace();}return jwt;}
}
2.處理消息體。
消息體格式如下
{"aud": "<
開發者Client ID
>","iss": "id.cloud.huawei.com","iat": 1727619834,"jti": "6672ed7d5c5e4c3c92f343ecac40f326","events": {"https://schemas.openid.net/secevent/risc/event-type/account-purged": {"subject": {"sub": "<
觸發事件用戶的UnionID
>","subject_type": "iss_sub","extra": "<
觸發事件用戶的OpenID
>","iss": "id.cloud.huawei.com"}}}
}
其中,各字段含義如下:
?
本文主要引用參考HarmonyOS官方網站