鸿蒙应用元服务开发-Account Kit未成年人模式订阅和处理用户信息变更

一、概述

通过订阅用户信息变更,您可以接收有关用户及其账户的重要更新。当用户取消元服务的授权信息、注销华为账号时,华为账号服务器会发送通知到元服务,元服务可以根据通知消息进行自身业务处理。

二、用户信息变更事件介绍

三、订阅用户信息变更

订阅步骤如下:

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 ID
        String 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>() {
                        @Override
                        public @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);
                    }
                }
                @Override
                public Key resolveSigningKey(JwsHeader jwsHeader, Claims claims) {
                    return getPublicKey(jwsHeader);
                }
                @Override
                public 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官方网站

相关推荐
风行無痕3 分钟前
Ubuntu Linux系统配置账号无密码sudo
linux·服务器·ubuntu
沙振宇43 分钟前
【Web】使用Vue3开发鸿蒙的HelloWorld!
前端·华为·harmonyos
SZ1701102311 小时前
中继器的作用
服务器·网络·智能路由器
chenxy021 小时前
如何快速分享服务器上的文件
运维·服务器
重启就好2 小时前
【Ansible】模块详解
linux·服务器·ansible
SuperW3 小时前
Linxu实验五——NFS服务器
运维·服务器
promise5243 小时前
JVM之jcmd命令详解
java·linux·运维·服务器·jvm·bash·jcmd
果子⌂4 小时前
Linux系统入门第十二章 --Shell编程之正则表达式
linux·运维·服务器
海尔辛4 小时前
学习黑客5 分钟读懂Linux Filesystem Interaction Continued
linux·服务器·学习
学习2年半4 小时前
服务器mysql连接我碰到的错误
运维·服务器·mysql