菜单

EasyID 服务端 API 开发指导文档

下载

EasyID 服务端 API 开发指导文档

说明

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

imagepng

导引

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

应用侧需要做的事情

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

AM 有两种加密方式:

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

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

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

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

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

响应示例参考 响应示例

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

imagepng
imagepng

宁盾侧需要做的事情

创建 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对应的数据

JSON 复制代码
{"userId":"97e90bec-0e83-4819-a5ba-197512eadaf5",
"personalName":"邱建明",
"loginName":"60000013",
"mobiles":["13641079616"],
"groupPath":"/中国北京同仁堂(集团)有限责任公司集团本部——测试修改",
"groupId":"ab644174-bf64-4af7-8785-f3b07e1f811b",
"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":"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对应的数据

参数 类型 描述
parentGroupDescription String 父单位描述
parentGroupCode String 父单位code
state String 1为启用,0为禁用
uuid String
entryUUID String
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":"instanceType","value":"4","type":1},{"name":"objectGUID","value":"CF59C4DF-AFEE-2746-F810-AA71D871814A","type":1}]}

角色

参数 类型 描述
roleId String 角色ID
name String 角色名称
description String 角色描述
updateTime long 角色修改时间
createTime long 角色创建时间
JSON 复制代码
{
    "roleId": "8dde360b-06f9-4350-8859-c80b5fc33cf6",
    "name": "testRole3",
    "description": "",
    "updateTime": 1757415086127,
    "createTime": 1756892153000
}

接口响应示例

参数 类型 描述
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-09-06Powered by