菜单

EasyID应用对接文档

下载

说明

本文档规定了第三方应用如何根据 AM(宁盾平台,以下简称AM)的要求开发和集成 API 接口,用于接收 AM 上用户、用户组、角色的变更事件,AM 会用 HTTP 协议通过本接口向第三方应用进行实时推送,第三方应用只需要在 AM 上注册 easyApp 类型的应用并配置同步器,就能收到 AM 的实时通知消息。

Image

导引

如果您的应用需要接收AM(宁盾平台,以下简称AM)推送的用户、组织、角色的信息变更,您只需要做下面两步操作:

应用侧需要做的事情

根据此文档要求开发对应接口,用于接收 AM 推送用户、用户组、角色的变更消息。变更消息经过了加密 ,所以应用接收到消息后要先解密数据,解密方式及请求示例如下:

AM 有两种加密方式:

  1. 对称加密:若注册应用时选择加密方式为对称加密,则需要配置一个共享秘钥,AM 会使用 AES 算法加密数据,加密示例参考 开启对称加密,解密方式参考 对称加解密流程 和 接口代码示例

  2. 非对称加密:若注册应用时选择加密方式为非对称加密,则需要配置一个 RSA 公钥,AM 会使用公钥先加密一个对称秘钥 A,会把 A 加密后的密文 B 放在了HTTP请求头中,应用方需要先通过私钥解密 B 拿到 A,再通过 A 解密数据。加密示例参考 开启非对称加密 ,解密方式参考非对称加解密流程 和 接口代码示例

应用解出明文数据后,再将数据转换成符合您系统的数据格式,并进行相应的业务处理

根据请求参数进行相应的处理,参考 操作类型对应表

原始数据结构参考 数据结构示例

响应示例参考 响应示例

若处理中发生错误,状态码示例参考 接口状态码

Image
Image

宁盾侧需要做的事情

创建 easyApp 类型的应用,完成后选择账号同步,配置 Notify Url 接口地址,即应用接收事件的地址。

此接口为第一步开发的接口,AM 所有信息变更事件都会推送到这个接口上,不同的事件会通过 operation 参数进行区分,所以仅需提供一个接口即可,即 Notify Url,示例: http://1.1.1.1:80/app/rest/am/notify

选择加密方式

按照您的需要选择对称加密 / 非对称加密

秘钥说明

若选择对称加密则需要提供一个共享秘钥,示例如下。

若选择非对称加密则需要提供Base64编码后的 RSA 公钥,并在您的应用程序中保存 RSA 私钥,从而才能正确用私钥解密请求数据,示例如下。

接口请求示例:

开启对称加密

参数 类型 描述
operation int 操作类型,详细值如下
appId string 应用id
data string 实际数据

操作类型对照表

参数 值 类型 说明
operation 101 int
添加用户
102 更新用户
103 删除用户
201 添加组
202 更新组
203 删除组
301 添加角色
302 更新角色
303 删除角色

宁盾完整请求

JSON 复制代码
POST
contentType: application/json
Body  请求体
{"operation":101,"appId":"test","data":"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"}

对称加密共享秘钥(示例)

Plain Text 复制代码
7917c3d5-f047-450e-9c7e-b849e7937034

使用共享秘钥SHA-256对密钥进行哈希,在哈希后的byte\[\]中取前16位字节(示例)

JSON 复制代码
-25, -92, -75, 32, 91, 91, -42, -7, -86, 96, -75, 52, 15, 91, 112, -106

加密前数据(示例)

JSON 复制代码
{
    "userId": "bf885ae9-f6b3-4b5f-86f7-8e6fbb073141",
    "personalName": "测试姓名",
    "loginName": "test1",
    "identityStoreId": "ad69bb2e-e00a-4555-bc28-2f881f1fb712",
    "mobiles": ["12345678901","12345678910"],
    "email": "2250606528@qq.com",
    "groupPath": "/testGroup",
    "groupId": "d1e5f244-88ff-4ee3-af63-46d837adf575",
    "roleInfos": [
        {
            "roleId": "4f41dfe8-522f-46ff-83c6-9179fa2db6f3",
            "name": "testRole2",
            "roleGroupPath": ""
        },
        {
            "roleId": "8dde360b-06f9-4350-8859-c80b5fc33cf6",
            "name": "testRole1",
            "roleGroupPath": ""
        }
    ],
    "enabled": true,
    "locked": false,
    "changePasswordAtNextLogin": false,
    "number": "testNumber1",
    "department": "testDepartment",
    "company": "testCompany",
    "title": "testTitle",
    "customAttributes": [
        {
            "name": "positionView",
            "value": "[{     \"jobState\": \"test\",     \"jobCode\": \"test\",     \"jobTypeCode\": \"test\",     \"jobGroupCode\": \"test\",     \"jobGroupName\": \"test\",     \"jobDepartmentCode\": \"test\",     \"jobDepartmentName\": \"test\",     \"position\": \"test\",     \"job\": \"test\",     \"jobStartTime\": \"test\",     \"jobEndTime\":\"test\"                                    }]",
            "type": 1
        }
    ],
    "createTime": 1756891171000,
    "updateTime": 1756891171000
}

加密后data数据(示例)

Plain Text 复制代码
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

开启非对称加密

参数 类型 描述
operation int 操作类型,详细值如下
appId string 应用id
data string 实际数据
应用方从请求头拿到 secret 后先通过RSA私钥解密出明文对称秘钥,再使用明文对称秘钥解密data拿到明文数据

操作类型对照表

参数 值 类型 说明
operation 101 int
添加用户
102 更新用户
103 删除用户
201 添加组
202 更新组
203 删除组
301 添加角色
302 更新角色
303 删除角色

宁盾完整请求

JSON 复制代码
POST
contentType: application/json
Header 请求头参数
   // 根据应用方提供的 RSA 公钥加密的对称秘钥的密文
  "secret": "edGTecB3dLcbtu2hbUJxvkGMWFAsI570JvxnZA3JR1VZ1G7HiAms3Jd87UHifDscQ7mgBnzBtfiuYoecyxEszgHgfpEmK7ur8JMYpDZEivL7h9fwQ49/Ne+yHhSAsCyZKzHlIeRwebtaqdNRVsG3I047swTAE+E+a3orYoTFhWytd/oZ1d8tSVkBw7iDbRf4VOx2rWRVD/HVizjH5ZIgF79iq0cc/pg0vQl5vYbhCmGWrIpTQI7B7LcZ7WWQG/ZzAmpVEG9VZwZp0rVILqtgO29vukyC722GWxJ/BHGLDETZ+00EErewNITHPH8UiU3OwMfynp/83CBfha3ZJO5R/w==" 
   
Body  请求体
{
    "operation": 101, // 参见 operation 表
    "appId" : "dec19ca2c41b",   // 应用ID
    "data": "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"  
    // 应用方拿到 secret 后先通过RSA私钥解密出明文对称秘钥,再使用明文对称秘钥解密data拿到明文数据
}

RSA公钥 BASE64

Plain Text 复制代码
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt+acbvTu/x9G0zdojfWRhpu2a5hO/4rVhhzzPInWKX4I2TGRHp2aggEBRTv+N3EuYX8NebeDzW/smTiNy6JSKedAfDnSoMgOecwS1M+sg6FhVDrfI8GqelZ/gCpI+aX+rZAsbQo0jLqEUrZbD1r5rJ8vAR2cC+AP1wZYLRx9UogtLZ5Yqc8OtOgX3sKnyCJHALU4vpsL7ItA7iT3BOzFFmy3o3Xbs/Uhsm3+jYyvSCQIb7JIap6BcEPu5izYMq0KpKmqh83C9AyTwbuBW0ph1NtzXhqn/EIdabEOIJtGMTrfjyl3iKr7kuzaZXu6yaOkgUT1ajuzoTpORegDCdZtTwIDAQAB

加密前secret

Plain Text 复制代码
7917c3d5-f047-450e-9c7e-b849e7937034

加密后secret

Plain Text 复制代码
Z88BzDnp6josWJW1Suc8yUo1jWu4rWiREvlAoZHL0EpwlcaXJg20y6xVc9ifyHkz5+dWNmcbYAr+teG68K/bn6mMjTP07xp5GK0/ddKnRGNg/qin8f5knqwcKXk8zjl4FuTTRShm6tF5MIFrgVbK3rNkL3Fp3Q1AT3pxzs41B0Miee6YVIGlWnYfnnzlqtInd6RS+zMog41PxDueMj8f+nmd7noDycfI8TG1IMH2+q7/rf69nCFCDAs/NU7vSvy2T9l2qmZLHqMka9kvJJ+pgSpCKjSSqWMmCKazNC6P++E3nozDwQyujU4MDF8dZLlE/TfMAAkPT8Nb6+aX/ny0DA==

加密前data

JSON 复制代码
{
    "userId": "bf885ae9-f6b3-4b5f-86f7-8e6fbb073141",
    "personalName": "测试姓名",
    "loginName": "test1",
    "identityStoreId": "ad69bb2e-e00a-4555-bc28-2f881f1fb712",
    "mobiles": ["12345678901","12345678910"],
    "email": "2250606528@qq.com",
    "groupPath": "/testGroup",
    "groupId": "d1e5f244-88ff-4ee3-af63-46d837adf575",
    "roleInfos": [
        {
            "roleId": "4f41dfe8-522f-46ff-83c6-9179fa2db6f3",
            "name": "testRole2",
            "roleGroupPath": ""
        },
        {
            "roleId": "8dde360b-06f9-4350-8859-c80b5fc33cf6",
            "name": "testRole1",
            "roleGroupPath": ""
        }
    ],
    "enabled": true,
    "locked": false,
    "changePasswordAtNextLogin": false,
    "number": "testNumber1",
    "department": "testDepartment",
    "company": "testCompany",
    "title": "testTitle",
    "customAttributes": [
        {
            "name": "positionView",
            "value": "{     \"jobState\": \"test\",     \"jobCode\": \"test\",     \"jobTypeCode\": \"test\",     \"jobGroupCode\": \"test\",     \"jobGroupName\": \"test\",     \"jobDepartmentCode\": \"test\",     \"jobDepartmentName\": \"test\",     \"position\": \"test\",     \"job\": \"test\",     \"jobStartTime\": \"test\",     \"jobEndTime\":\"test\"                                    }",
            "type": 1
        }
    ],
    "createTime": 1756891171000,
    "updateTime": 1756891171000
}

加密后data

Plain Text 复制代码
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

数据结构示例

用户

基本属性

额外属性

customAttributes对应的数据,,name是参数名,value是参数值。根据客户场景来不固定

示例

JSON 复制代码
{"userId":"97e90bec-0e83-4819-a5ba-197512eadaf5",
"personalName":"qiujianming",
"loginName":"60000013",
"mobiles":["13641079616"],
"groupPath":"/中国北京同仁堂(集团)有限责任公司集团本部——测试修改",
"groupId":"ab644174-bf64-4af7-8785-f3b07e1f811b",
"password":"123456"
"roleInfos":[],
"enabled":true,
"locked":false,
"changePasswordAtNextLogin":false,
"idCardNumber":"411322196811054635",
"department":"中国北京同仁堂(集团)有限责任公司党委办公室(党委巡察办公室)",
"customAttributes":[
{"name":"userGroupName","value":"中国北京同仁堂(集团)有限责任公司战略管理部(董事会办公室)","type":1},
{"name":"userState","value":"11","type":1},
{"name":"gender","value":"2","type":1},
{"name":"positionView","value":"[{\"jobGroupName\":\"中国北京同仁堂(集团)有限责任公司战略管理部(董事会办公室)\",\"jobGroupCode\":\"20000003\",\"jobState\":\"1\",\"jobCode\":\"9000000012\",\"jobTypeCode\":\"1\",\"position\":\"党支部委员会\",\"job\":\"党委书记\",\"jobDepartmentName\":\"中国北京同仁堂(集团)有限责任公司战略管理部(董事会办公室)\",\"jobStartTime\":\"2025-09-01 00:00:00\",\"jobDepartmentCode\":\"20000003\",\"jobEndTime\":\"\"},{\"jobGroupName\":\"中国北京同仁堂(集团)有限责任公司战略管理部(董事会办公室)\",\"jobGroupCode\":\"20000003\",\"jobState\":\"0\",\"jobCode\":\"9000000011\",\"jobTypeCode\":\"1\",\"position\":\"党委书记\",\"job\":\"党委书记\",\"jobDepartmentName\":\"中国北京同仁堂(集团)有限责任公司战略管理部(董事会办公室)\",\"jobStartTime\":\"2023-10-11 00:00:00\",\"jobDepartmentCode\":\"20000003\",\"jobEndTime\":\"2025-09-01 00:00:00\"}]","type":1},
{"name":"userDepartmentCode","value":"20000005","type":1},{"name":"state","value":"1","type":1},
{"name":"countryCode","value":"0","type":1},
{"name":"userDepartmentName","value":"中国北京同仁堂(集团)有限责任公司党委办公室(党委巡察办公室)","type":1},
{"name":"uuid","value":"KHNOo0jlZ8hm2fKN15MzOQvicGquYKdt","type":1},{"name":"userGroupCode","value":"20000001","type":1},{"name":"entryUUID","value":"bcd5598d-f44d-4e01-a173-eb23b6cf23c3","type":1},
{"name":"personalName","value":"邱建明","type":1},
{"name":"joinedDate","value":"2025-09-01","type":1},
"createTime":1761035146000,
"updateTime":1761035146000}

组织

基本属性

参数 类型 描述
groupId String 当前用户组id
name String 当前用户组名称
parentId String 当前用户组的父用户组id
groupPath String 当前用户组的组织路径不包括用户源
groupIdPath String 当前用户组的组织id路径不包括自己

额外属性

customAttributes对应的数据,根据客户场景来不固定

示例

JSON 复制代码
{"groupId":"ab644174-bf64-4af7-8785-f3b07e1f811b","name":"中国北京同仁堂(集团)有限责任公司集团本部——测试修改",
"groupPath":"/中国北京同仁堂(集团)有限责任公司集团本部——测试修改",
"groupIdPath":"67b8d648-5699-433e-aa0b-d19d77cb3de4",
"customAttributes":[{"name":"parentGroupDescription","value":"中国北京同仁堂(集团)有限责任公司","type":1},{"name":"parentGroupCode","value":"20000000","type":1},{"name":"state","value":"1","type":1},{"name":"uuid","value":"lgxVJeZBKoGWu0o5CK5oVPatxF9A9Ymc","type":1},{"name":"entryUUID","value":"82cecb25-b12d-48a5-9bd1-b023a8ec2f6f","type":1},{"name":"code","value":"20000001","type":1},
{"name":"groupId","value":"020082dc-7da6-43d7-a6d0-206ef679a4fa","type":1},
{"name":"orgType","value":"分公司","type":1}
]}

接口响应示例

参数 类型 描述
success boolean 若失败,则为false
code String 状态码,宁盾会根据状态码进行相应的处理
message String 如果有报错将报错信息放到message中
Java 复制代码
{
    success:true,   //若失败,则为false
    "code":0,       //状态码,宁盾会根据状态码进行相应的处理
    "message":""    //如果有报错将报错信息放到message中
}

响应状态码

状态码 错误原因 备注
0 成功
500 系统错误 出现不在其他状态码范围内的任何错误,如数据库相关错误,网络io相关错误
-100 数据重复 常见于添加用户,添加角色,添加组织,如添加用户时用户名重复
-101 数据不存在 常见于更新用户,更新角色,更新组织,如更新用户时找不到对应用户
-200 解析data失败
-201 解析secret失败

应用加解密代码示例

对称加密类型

使用共享秘钥进行SHA-256对密钥进行哈希,并取前16位作为AES公钥解密请求体body中data对应的数据,共享秘钥由第三方应用提供。

非对称加密类型

先使用Base64之后的RSA私钥解密请求头header中secret对应的数据,用解密出的secret作为AES公钥解密请求体body中data对应的数据。

代码示例JAVA为例

对称加解密流程
Java 复制代码
*// AES加密方法*
public static String encryptAES(String key, String data) throws Exception {
    *// 使用SHA-256对密钥进行哈希*
*    *MessageDigest sha = MessageDigest.*getInstance*("SHA-256");
    byte[] keyBytes = sha.digest(key.getBytes(StandardCharsets.*UTF_8*));

    *// 只使用前16个字节作为密钥*
*    *byte[] aesKeyBytes = Arrays.*copyOf*(keyBytes, 16);
    SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(aesKeyBytes, "AES");

    Cipher cipher = Cipher.*getInstance*("AES");
    cipher.init(Cipher.*ENCRYPT_MODE*, secretKey);

    byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.*UTF_8*));
    return Base64.*getEncoder*().encodeToString(encryptedBytes);
}

*private static String decryptAES(String key, String encodedData) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
    // 使用SHA-256对密钥进行哈希
    MessageDigest sha = MessageDigest.getInstance("SHA-256");
    byte[] keyBytes = sha.digest(key.getBytes(StandardCharsets.UTF_8));

    // 只使用前16个字节作为密钥
    byte[] truncatedKeyBytes = Arrays.copyOf(keyBytes, 16);

    // 生成SecretKey对象
    SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(truncatedKeyBytes, "AES");

    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, secretKey);

    byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encodedData));
    return new String(decryptedBytes, StandardCharsets.UTF_8);
**}*

public static void testAESEncryption() throws Exception {
    //任意长度秘钥均可
*    *String aesKeyString = "123456";

    String data = "Hello, World!";//请求体中的data对应的数据

    *// 使用AES加密数据*
*    *String encryptedAES = *encryptAES*(aesKeyString, data);
    System.*out*.println("Encrypted AES: " + encryptedAES);


    *// 使用解密后的AES密钥解密数据*
*    *String decryptedData = *decryptAES*(aesKeyString,encryptedAES);
    System.*out*.println("Decrypted Data: " + decryptedData);
}
非对称加解密流程
Java 复制代码
*// AES加密方法*
private static String encryptAES(String key, String data) throws Exception {
    *// 使用SHA-256对密钥进行哈希*
*    *MessageDigest sha = MessageDigest.*getInstance*("SHA-256");
    byte[] keyBytes = sha.digest(key.getBytes(StandardCharsets.*UTF_8*));

    *// 只使用前16个字节作为密钥*
*    *byte[] aesKeyBytes = Arrays.*copyOf*(keyBytes, 16);
    SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(aesKeyBytes, "AES");

    Cipher cipher = Cipher.*getInstance*("AES");
    cipher.init(Cipher.*ENCRYPT_MODE*, secretKey);

    byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.*UTF_8*));
    return Base64.*getEncoder*().encodeToString(encryptedBytes);
}

*//AES解密方法
private static String decryptAES(String key, String encodedData) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
    // 使用SHA-256对密钥进行哈希
    MessageDigest sha = MessageDigest.getInstance("SHA-256");
    byte[] keyBytes = sha.digest(key.getBytes(StandardCharsets.UTF_8));

    // 只使用前16个字节作为密钥
    byte[] truncatedKeyBytes = Arrays.copyOf(keyBytes, 16);

    // 生成SecretKey对象
    SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(truncatedKeyBytes, "AES");

    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, secretKey);

    byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encodedData));
    return new String(decryptedBytes, StandardCharsets.UTF_8);
**}*

*// RSA加密方法*
public static String encryptRSA(String key, String data) throws Exception {
    byte[] publicKeyBytes = Base64.*getDecoder*().decode(key);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
    KeyFactory keyFactory = KeyFactory.*getInstance*("RSA");
    PublicKey rsaPublicKey = keyFactory.generatePublic(keySpec);

    Cipher cipher = Cipher.*getInstance*("RSA");
    cipher.init(Cipher.*ENCRYPT_MODE*, rsaPublicKey);

    byteencryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.*UTF_8*));
    return Base64.*getEncoder*().encodeToString(encryptedBytes);
}

*// RSA解密方法*
public static String decryptRSA(String key, String encryptedData) throws Exception {
    byte[] privateKeyBytes = Base64.*getDecoder*().decode(key);
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
    KeyFactory keyFactory = KeyFactory.*getInstance*("RSA");
    PrivateKey rsaPrivateKey = keyFactory.generatePrivate(keySpec);

    Cipher cipher = Cipher.*getInstance*("RSA");
    cipher.init(Cipher.*DECRYPT_MODE*, rsaPrivateKey);

    bytedecryptedBytes = cipher.doFinal(Base64.*getDecoder*().decode(encryptedData));
    return new String(decryptedBytes, StandardCharsets.*UTF_8*);
}

private static String generateAESKey() throws NoSuchAlgorithmException {
    KeyGenerator keyGen = KeyGenerator.*getInstance*("AES");
    keyGen.init(256);
    SecretKey aesKey = keyGen.generateKey();
    return Base64.*getEncoder*().encodeToString(aesKey.getEncoded());
}

*// 测试方法*
public static void testEncryption() throws Exception {
    *// 生成AES密钥*
*    *String aesKeyString = *generateAESKey*();

    *// 生成RSA密钥对*
*    *KeyPairGenerator keyPairGen = KeyPairGenerator.*getInstance*("RSA");
    keyPairGen.initialize(2048);
    KeyPair keyPair = keyPairGen.generateKeyPair();
    String publicKeyString = Base64.*getEncoder*().encodeToString(keyPair.getPublic().getEncoded());
    String privateKeyString = Base64.*getEncoder*().encodeToString(keyPair.getPrivate().getEncoded());

    String data = "Hello, World!";

    *// 使用AES加密数据*
*    *String encryptedAES = *encryptAES*(aesKeyString, data);
    System.*out*.println("Encrypted AES: " encryptedAES);

    *// 使用RSA加密AES密钥*
*    *String encryptedAESKey = *encryptRSA*(publicKeyString, aesKeyString);
    System.*out*.println("Encrypted AES Key: " encryptedAESKey);

    *// 使用RSA解密请求头中的AES密钥secret*
*    *String decryptedAESKey = *decryptRSA*(privateKeyString, encryptedAESKey);
    System.*out*.println("Decrypted AES Key: " decryptedAESKey);

    *// 使用解密后的AES密钥解密data数据*
*    *String decryptedData = *decryptAES*(decryptedAESKey, encryptedAES);
    System.*out*.println("Decrypted Data: " decryptedData);
}

应用侧接口代码示例,仅供参考JAVA为例

如果选择对称加密方式:
Java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;

protected static final ObjectMapper *OM *= new ObjectMapper();

@RequestMapping(value = "/notify")
public ResponseData receiveData() {
    try {
         //解析获得请求的请求体 js
        JsonNode js = *OM*.readTree(getRequest().getInputStream());
      
        //获取js中的data节点对应的加密数据
        String encodedData = js.get("data").asText();

        //根据应用程序内之前保存的秘钥解密加密数据,生成方法可参考generateAESKey
        String key = "Yovto2FgLf7j79T97Y3Mg92QghFU9DyoVKX5Lq1RhOM=";
        
        //解密数据后,根据operation进行相应的处理
        String data = decryptAES(key, encodedData);
        int operation = js.get("operation").asint();
        
        //解析appId 定位具体应用
        String appId = js.get("appId").asText();
        process(data, operation);
        
        return ResponseData.*successData*();
    } catch (Exception e) {
        return ResponseData.*errorData*(e.getMessage());
    } 
}

// 生成AES秘钥方法
private static String generateAESKey() throws NoSuchAlgorithmException {
    KeyGenerator keyGen = KeyGenerator.*getInstance*("AES");
    keyGen.init(256);
    SecretKey aesKey = keyGen.generateKey();
    return Base64.*getEncoder*().encodeToString(aesKey.getEncoded());
}

*// AES解密方法*
public  String decryptAES(String key, String encryptedData) throws Exception {
    byte[] keyBytes = Base64.*getDecoder*().decode(key);
    SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(keyBytes, "AES");

    Cipher cipher = Cipher.*getInstance*("AES");
    cipher.init(Cipher.*DECRYPT_MODE*, secretKey);

    byte[] decryptedBytes = cipher.doFinal(Base64.*getDecoder*().decode(encryptedData));
    return new String(decryptedBytes, StandardCharsets.*UTF_8*);
}

public void process(String data, int operation) {
    //可参考 解析data示例
    
    if (operation == 101){
        //添加用户逻辑处理
    } else if(){
        //业务代码
    }
}
如果选择非对称加密方式:
Java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;

protected static final ObjectMapper *OM *= new ObjectMapper();

@RequestMapping(value = "/notify")
public ResponseData receiveData() {
    try {
        //在请求头中获取secret
        String encodedSecret = getRequest().getHeader("secret");  
        
        //根据应用程序内之前保存的私钥rsa解密加密后的secret,生成方法可参考generateRSAKey
        String privateKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt+acbvTu/x9G0zdojfWRhpu2a5hO/4rVhhzzPInWKX4I2TGRHp2aggEBRTv+N3EuYX8NebeDzW/smTiNy6JSKedAfDnSoMgOecwS1M+sg6FhVDrfI8GqelZ/gCpI+aX+rZAsbQo0jLqEUrZbD1r5rJ8vAR2cC+AP1wZYLRx9UogtLZ5Yqc8OtOgX3sKnyCJHALU4vpsL7ItA7iT3BOzFFmy3o3Xbs/Uhsm3+jYyvSCQIb7JIap6BcEPu5izYMq0KpKmqh83C9AyTwbuBW0ph1NtzXhqn/EIdabEOIJtGMTrfjyl3iKr7kuzaZXu6yaOkgUT1ajuzoTpORegDCdZtTwIDAQAB";
        String secret = decryptRSA(privateKey,encodedSecret);
        
         //解析获得请求的请求体 js
        JsonNode js = *OM*.readTree(getRequest().getInputStream());
        
        //获取js中的data节点对应的加密数据
        String encodedData = js.get("data").asText();

        //使用解密后的secret作为秘钥aes解密数据后,根据operation进行相应的处理
        String data = decryptAES(secret,encodedData);
        int operation = js.get("operation").asint();
        
        //解析appId 定位具体应用
        String appId = js.get("appId").asText();
        process(data,operation);
        
        return ResponseData.*successData*();
    } catch (Exception e) {
        return ResponseData.*errorData*(e.getMessage());
    } 
}

//生成RSA秘钥方法 publicKeyString为公钥,privateKeyString为私钥
private static void generateRSAKey() {
    KeyPairGenerator keyPairGen = KeyPairGenerator.*getInstance*("RSA");
    keyPairGen.initialize(2048);
    KeyPair keyPair = keyPairGen.generateKeyPair();
    String publicKeyString = Base64.*getEncoder*().encodeToString(keyPair.getPublic().getEncoded());
    String privateKeyString = Base64.*getEncoder*().encodeToString(keyPair.getPrivate().getEncoded());
}

*// AES解密方法*
public  String decryptAES(String key, String encryptedData) throws Exception {
    byte[] keyBytes = Base64.*getDecoder*().decode(key);
    SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(keyBytes, "AES");

    Cipher cipher = Cipher.*getInstance*("AES");
    cipher.init(Cipher.*DECRYPT_MODE*, secretKey);

    byte[] decryptedBytes = cipher.doFinal(Base64.*getDecoder*().decode(encryptedData));
    return new String(decryptedBytes, StandardCharsets.*UTF_8*);
}

*// RSA解密方法*
public static String decryptRSA(String key, String encryptedData) throws Exception {
    byte[] privateKeyBytes = Base64.*getDecoder*().decode(key);
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
    KeyFactory keyFactory = KeyFactory.*getInstance*("RSA");
    PrivateKey rsaPrivateKey = keyFactory.generatePrivate(keySpec);

    Cipher cipher = Cipher.*getInstance*("RSA");
    cipher.init(Cipher.*DECRYPT_MODE*, rsaPrivateKey);

    byte[] decryptedBytes = cipher.doFinal(Base64.*getDecoder*().decode(encryptedData));
    return new String(decryptedBytes, StandardCharsets.*UTF_8*);
}

public void process(String data, int operation) {
    ObjectNode dataNode = *OM*.readValue(data, ObjectNode.class);
    if (operation == 101){
        //添加用户逻辑处理
    } else if(){
        //业务代码
    }
}
解析data示例
Java 复制代码
*import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.ArrayList;
import java.util.List;


public class Test {
    private static ObjectMapper OM = new ObjectMapper();
    public static void main(String[] args) {
       String data = "{\n" +
               "    \"userId\": \"bf885ae9-f6b3-4b5f-86f7-8e6fbb073141\",\n" +
               "    \"personalName\": \"测试姓名\",\n" +
               "    \"loginName\": \"test1\",\n" +
               "    \"identityStoreId\": \"ad69bb2e-e00a-4555-bc28-2f881f1fb712\",\n" +
               "    \"mobiles\": [\n" +
               "        \"12345678901\",\n" +
               "        \"12345678910\"\n" +
               "    ],\n" +
               "    \"email\": \"2250606528@qq.com\",\n" +
               "    \"groupPath\": \"/testGroup\",\n" +
               "    \"groupId\": \"d1e5f244-88ff-4ee3-af63-46d837adf575\",\n" +
               "    \"roleInfos\": [\n" +
               "        {\n" +
               "            \"roleId\": \"4f41dfe8-522f-46ff-83c6-9179fa2db6f3\",\n" +
               "            \"name\": \"testRole2\",\n" +
               "            \"roleGroupPath\": \"\"\n" +
               "        },\n" +
               "        {\n" +
               "            \"roleId\": \"8dde360b-06f9-4350-8859-c80b5fc33cf6\",\n" +
               "            \"name\": \"testRole1\",\n" +
               "            \"to roleGroupPath\": \"\"\n" +
               "        }\n" +
               "    ],\n" +
               "    \"enabled\": true,\n" +
               "    \"locked\": false,\n" +
               "    \"changePasswordAtNextLogin\": false,\n" +
               "    \"number\": \"testNumber1\",\n" +
               "    \"department\": \"testDepartment\",\n" +
               "    \"company\": \"testCompany\",\n" +
               "    \"title\": \"testTitle\",\n" +
               "    \"customAttributes\": [\n" +
               "        {\n" +
               "            \"name\": \"positionView\",\n" +
               "            \"value\": \"{     \\\"jobState\\\": \\\"test\\\",     \\\"jobCode\\\": \\\"test\\\",     \\\"jobTypeCode\\\": \\\"test\\\",     \\\"jobGroupCode\\\": \\\"test\\\",     \\\"jobGroupName\\\": \\\"test\\\",     \\\"jobDepartmentCode\\\": \\\"test\\\",     \\\"jobDepartmentName\\\": \\\"test\\\",     \\\"position\\\": \\\"test\\\",     \\\"job\\\": \\\"test\\\",     \\\"jobStartTime\\\": \\\"test\\\",     \\\"jobEndTime\\\":\\\"test\\\"                                    }\",\n" +
               "            \"type\": 1\n" +
               "        }\n" +
               "    ],\n" +
               "    \"createTime\": 1756891171000,\n" +
               "    \"updateTime\": 1756891171000\n" +
               "}" ;

        try {
            JsonNode dataNode = OM.readTree(data);

            // 基本字段变量定义与赋值
            String userId = dataNode.get("userId").asText();
            String personalName = dataNode.get("personalName").asText();
            String loginName = dataNode.get("loginName").asText();
            String identityStoreId = dataNode.get("identityStoreId").asText();
            String email = dataNode.get("email").asText();
            String groupPath = dataNode.get("groupPath").asText();
            String groupId = dataNode.get("groupId").asText();
            boolean enabled = dataNode.get("enabled").asBoolean();
            boolean locked = dataNode.get("locked").asBoolean();
            boolean changePasswordAtNextLogin = dataNode.get("changePasswordAtNextLogin").asBoolean();
            String number = dataNode.get("number").asText();
            String department = dataNode.get("department").asText();
            String company = dataNode.get("company").asText();
            String title = dataNode.get("title").asText();
            long createTime = dataNode.get("createTime").asLong();
            long updateTime = dataNode.get("updateTime").asLong();

            // 数组类型字段 - mobiles
            List<String> mobiles = new ArrayList<>();
            JsonNode mobilesNode = dataNode.get("mobiles");
            if (mobilesNode.isArray()) {
                for (int i = 0; i < mobilesNode.size(); i++) {
                    mobiles.add(mobilesNode.get(i).asText());
                }
            }

            JsonNode roleInfosNode = dataNode.get("roleInfos");


            JsonNode customAttrsNode = dataNode.get("customAttributes");
            if (customAttrsNode.isArray()) {
                for (JsonNode attrNode : customAttrsNode) {
                    // 找到name为positionView的属性
                    if ("positionView".equals(attrNode.get("name").asText())) {
                        // 获取value字段的JSON字符串
                        String positionViewJson = attrNode.get("value").asText();
                        // 解析嵌套的JSON
                        JsonNode positionNode = OM.readTree(positionViewJson);

                        // 提取各个字段值
                        String jobState = positionNode.get("jobState").asText();
                        String jobCode = positionNode.get("jobCode").asText();
                        String jobTypeCode = positionNode.get("jobTypeCode").asText();
                        String jobGroupCode = positionNode.get("jobGroupCode").asText();
                        String jobGroupName = positionNode.get("jobGroupName").asText();
                        String jobDepartmentCode = positionNode.get("jobDepartmentCode").asText();
                        String jobDepartmentName = positionNode.get("jobDepartmentName").asText();
                        String position = positionNode.get("position").asText();
                        String job = positionNode.get("job").asText();
                        String jobStartTime = positionNode.get("jobStartTime").asText();
                        String jobEndTime = positionNode.get("jobEndTime").asText();

                    }
                }
            }
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
**    }*
    }
最近修改: 2026-10-22Powered by