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

导引
如果您的应用需要接收AM(宁盾平台,以下简称AM)推送的用户、组织、角色的信息变更,您只需要做下面两步操作:
1. 应用侧需要做的事情
1.1 根据此文档要求开发对应接口,用于接收 AM 推送用户、用户组、角色的变更消息。变更消息经过了加密 ,所以应用接收到消息后要先解密数据,解密方式及请求示例如下:
AM 有两种加密方式:
对称加密:若注册应用时选择加密方式为对称加密,则需要配置一个共享秘钥,AM 会使用 AES 算法加密数据,加密示例参考 开启对称加密,解密方式参考 对称加解密流程 和 接口代码示例
非对称加密:若注册应用时选择加密方式为非对称加密,则需要配置一个 RSA 公钥,AM 会使用公钥先加密一个对称秘钥 A,会把 A 加密后的密文 B 放在了HTTP请求头中,应用方需要先通过私钥解密 B 拿到 A,再通过 A 解密数据。加密示例参考 开启非对称加密 ,解密方式参考非对称加解密流程 和 接口代码示例
1.2 应用解出明文数据后,再将数据转换成符合您系统的数据格式,并进行相应的业务处理
根据请求参数进行相应的处理,参考 操作类型对应表
原始数据结构参考 数据结构示例
响应示例参考 响应示例
若处理中发生错误,状态码示例参考 接口状态码


2. 宁盾侧需要做的事情
2.1 创建 easyApp 类型的应用,完成后选择账号同步,配置 Notify Url 接口地址,即应用接收事件的地址。
此接口为第一步开发的接口,AM 所有信息变更事件都会推送到这个接口上,不同的事件会通过 operation 参数进行区分,所以仅需提供一个接口即可,即 Notify Url,示例: http://1.1.1.1:80/app/rest/am/notify
2.2 选择加密方式
按照您的需要选择对称加密 / 非对称加密
2.3 秘钥说明
若选择对称加密则需要提供一个共享秘钥,示例如下。
若选择非对称加密则需要提供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 mobiles = new ArrayList(); JsonNode mobilesNode = dataNode.get("mobiles"); if (mobilesNode.isArray()) { for (int i = 0; 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(); } } } |