本文档规定了第三方应用如何根据 AM(宁盾平台,以下简称AM)的要求开发和集成 API 接口,用于接收 AM 上用户、用户组、角色的变更事件,AM 会用 HTTP 协议通过本接口向第三方应用进行实时推送,第三方应用只需要在 AM 上注册 easyApp 类型的应用并配置同步器,就能收到 AM 的实时通知消息。
如果您的应用需要接收AM(宁盾平台,以下简称AM)推送的用户、组织、角色的信息变更,您只需要做下面两步操作:
AM 有两种加密方式:
对称加密:若注册应用时选择加密方式为对称加密,则需要配置一个共享秘钥,AM 会使用 AES 算法加密数据,加密示例参考 开启对称加密,解密方式参考 对称加解密流程 和 接口代码示例
非对称加密:若注册应用时选择加密方式为非对称加密,则需要配置一个 RSA 公钥,AM 会使用公钥先加密一个对称秘钥 A,会把 A 加密后的密文 B 放在了HTTP请求头中,应用方需要先通过私钥解密 B 拿到 A,再通过 A 解密数据。加密示例参考 开启非对称加密 ,解密方式参考非对称加解密流程 和 接口代码示例
根据请求参数进行相应的处理,参考 操作类型对应表
原始数据结构参考 数据结构示例
响应示例参考 响应示例
若处理中发生错误,状态码示例参考 接口状态码
此接口为第一步开发的接口,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 | 删除角色 |
POST
contentType: application/json
Body 请求体
{"operation":101,"appId":"test","data":"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"}
对称加密共享秘钥(示例)
7917c3d5-f047-450e-9c7e-b849e7937034
使用共享秘钥SHA-256对密钥进行哈希,在哈希后的byte\[\]中取前16位字节(示例)
-25, -92, -75, 32, 91, 91, -42, -7, -86, 96, -75, 52, 15, 91, 112, -106
加密前数据(示例)
{
"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数据(示例)
s1moMbXClmqUg80yTmgVOtaBKdHneZH++3PZhgz1BPQhWjL1V2fq98pL4c24SiBuZI2wHsHYhRyJzvk9bJxrth0+0syBa6cwRcfucOvVJ8v1bpyDOISapsWrPj+Bzt+88mnPOqcJiBO+q8QrVbmpg4dHMNIeXP4cnvQcU7MPtrmKXcDUBTn6C6min8aTz/FXDmvxWjCjMTPm8674JrtmMN5WKCAVcUMbn1aYgvFISWCGxTIudpmrDGZDmTged3M2vs1f+yDhHFpMaHtwC94bIrpDQjFVIhoOTB05UtLNIheh+Z1vmr+CRU8ISelK3JdWQAVhhTXTAssxqsdoqcBZl7YlCkHM8ovcaDASNBsR8pz6WEPtMiCuOk7DIekzXjkA+iZcbDNhrUdSZcSVr6uD+8essl5BOoo4wVBfFrVBH2RBmaXyfmC8Qp05BrOF45Ken/9Ad9jG7eBrW8ggzcW3v/m96Xps5ouyimzQMFsjJoc0wncxm5517RbeX1CQzAvLyr0IF6CeVRFDlEzOy8knGt6Uz0Pj+u4AwzDgcRrl1zz/qeRVIV9xcyPamtQswR2ZYy+OD+RwfwwnvyxBh6nrnl9lP7Qfdr/fTlT89kc6aQdfDNlTSCzFB50fg4RFdKZq0Zoj5+sFCseb9W9NKWEKz3tBnt1sDb4i6IhtU5GJ9rKw1GA2vSdRU5mrXo9Z8JRV2QOtne5sO+ps4HLVAQgpbszsaTe33rqGWKH9YrHvZJ5lsA+I/FIPPdfk8KfGu28ympla/9dwmoWcD5sobpyZEfxEYghpNTTSeFFOd/fW+bUvCaAwDZcC0KCyCTW96C3nelt6C0JIqrLVjp/hB+qgm2G0Mjy6nCQDMmG+m7z42YWlb6MPXh1vtvl9zNeX6kIPuOVN93CyuF4iD6R/I7xRpGYAtDO21VOVag4RsgTq0QFMOeekRi7s0M+pkvkisstL0iXvWNQsvrGBB2/cmMXqfAzkVggMRQlZHynXHxTpQVWcoKmNPYJ3GYTSg10w992uZ28l08uAd/yZrY1CrH4uRzyo3HDrHAY9uSNO+KYkwxBQE/Ojs1EHlAJJvQoI+VLU4PPwRF8zP6+Mkjg4u2hmqETLp5dlTt73FRGzMYTU0/gm7TU25LHiXkjnZB9B+cVD3BY0xWPR3/g2kGhUBe0gt6q7nlXcNLHftT8BgNnU/H5xZJaPnib39H0RkfOaBmlyrfu1V2R7baOIogDxt2TFwBMCnjvi+3msWwFYKwOfKytTUqibf43pxxbTGuFInSajh6bDqtFiBbeK3uIpn5/ptNAhBoSRIG5t6lpPI9zZP5LsUzGkMteII7PJ4NaHXYbKLsQLZ34j8QumBARU27FTNJmAyQ53iBBYyeh0NZEpsRMfpPyDZoAI3GN5+0RBHjQYEUS6dag0xWwXw+RbXBd1wpjacCguiWs4E4mLBwsi8bqq1idGQrZ9JpjBVtw7v1TrhFAqfVQfH94aTgx9oZkpbSDy8Bky2WqwnMAbSp63yG3hrIpQ+Xq5RwUueKPit28Ddd6aFj1yP1s8vfb4OUsgTPCbdO69OkpfOJUptQ8mGqretkWImXUwdZnObwJLeqh0swbkagpDSj7E/QacpBowvAbniEOGgnEtXcDm/7fR/0XDUMdtwCmy0nWrdSw2DG+Cbd4pqG11P5YDZSBwe1Q+FpbuqDXcwKc4Xqb5iT8dqvlq4SYv9sBfPbevkgRsxTZ3GssAVsnRx1rhCvxCQGb7cMFwmSrn+XHtjaFskkUb4rxxAbTDmamvPoc5BSB6XAREVJpj6p0DZV2sgYif4PnpLBIcQyvm+q3oU1PZJyYQ37oK8Mk/QyRIR3ZQYAs06l6Og4TnXAv1BDtI23TP31J/sTWmSlSQCdjDESX4Wz/IU06iewiYq4diGuElKVQQq9nYvUWki3jewKucEG+s0sJbyZZ1LUusy9XSdNcWAzb41eIpNMf+2SaMzgi52MWG757A9teNFDnY/b2m3e/ZNfQiAKD0JNmhN62U8ptmNqgdVEKM2IlZ+zOCtGpcDmtUFjxH4zlLdAQ5YmCDjYrSfH256aIAtVRNkagwSd/Qi7Nha/QAj12uaW31kA6QQzQhLjTUmONp75+xqRa8GhzQU9GtPQCPXa5pbfWQDpBDNCEuNNTZ0mRlb6IR9rvjZ+nwm8zxMgWOGAbu+3UNkjj7pev12qMJ8qLoH9+vE+/P7aeBtZv1SolGi4i6+/1GpwmyDS0SUhCaryMgy4c+5yb6i2/T2kor/pusR1JvFwf9bDWeSpmFd80WO5FRBLuEWy5v3YmaoPQk2aE3rZTym2Y2qB1UQuSg2OwSa+xroja9YcFhSFygc7TqHhaDxlCRp8cquoAmCF7AV3l3JthvinAgtjhJsEoErv0yEqkkPm/gTFgJEXa0Ldpz8o4X/TkJn5G0U7pbKHMktxIQrtbHXHZD4rcq+ihzJLcSEK7Wx1x2Q+K3Kvqh2punPTsyclFWyaZCv/das279ku9YqCU021FF7mC/RrIAMuM1W2CaWeQ07i0VT7cbsmZuMG/zqcmPNzp9XzQuIHkQDaVJwSsjjPUQ4RFL4g==
| 参数 | 类型 | 描述 |
|---|---|---|
| operation | int | 操作类型,详细值如下 |
| appId | string | 应用id |
| data | string | 实际数据 应用方从请求头拿到 secret 后先通过RSA私钥解密出明文对称秘钥,再使用明文对称秘钥解密data拿到明文数据 |
| 参数 | 值 | 类型 | 说明 |
|---|---|---|---|
| operation | 101 | int |
添加用户 |
| 102 | 更新用户 | ||
| 103 | 删除用户 | ||
| 201 | 添加组 | ||
| 202 | 更新组 | ||
| 203 | 删除组 | ||
| 301 | 添加角色 | ||
| 302 | 更新角色 | ||
| 303 | 删除角色 |
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
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt+acbvTu/x9G0zdojfWRhpu2a5hO/4rVhhzzPInWKX4I2TGRHp2aggEBRTv+N3EuYX8NebeDzW/smTiNy6JSKedAfDnSoMgOecwS1M+sg6FhVDrfI8GqelZ/gCpI+aX+rZAsbQo0jLqEUrZbD1r5rJ8vAR2cC+AP1wZYLRx9UogtLZ5Yqc8OtOgX3sKnyCJHALU4vpsL7ItA7iT3BOzFFmy3o3Xbs/Uhsm3+jYyvSCQIb7JIap6BcEPu5izYMq0KpKmqh83C9AyTwbuBW0ph1NtzXhqn/EIdabEOIJtGMTrfjyl3iKr7kuzaZXu6yaOkgUT1ajuzoTpORegDCdZtTwIDAQAB
加密前secret
7917c3d5-f047-450e-9c7e-b849e7937034
加密后secret
Z88BzDnp6josWJW1Suc8yUo1jWu4rWiREvlAoZHL0EpwlcaXJg20y6xVc9ifyHkz5+dWNmcbYAr+teG68K/bn6mMjTP07xp5GK0/ddKnRGNg/qin8f5knqwcKXk8zjl4FuTTRShm6tF5MIFrgVbK3rNkL3Fp3Q1AT3pxzs41B0Miee6YVIGlWnYfnnzlqtInd6RS+zMog41PxDueMj8f+nmd7noDycfI8TG1IMH2+q7/rf69nCFCDAs/NU7vSvy2T9l2qmZLHqMka9kvJJ+pgSpCKjSSqWMmCKazNC6P++E3nozDwQyujU4MDF8dZLlE/TfMAAkPT8Nb6+aX/ny0DA==
加密前data
{
"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
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
customAttributes对应的数据,,name是参数名,value是参数值。根据客户场景来不固定
{"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对应的数据,根据客户场景来不固定
{"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中 |
{
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对应的数据。
*// 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);
}
*// 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);
}
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(){
//业务代码
}
}
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(){
//业务代码
}
}
*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();
}
** }*
}