杭州统一监督组件

组件


组件接口鉴权说明文档

<p>[TOC]</p> <h5>接口对接域名</h5> <p>&gt;测试环境</p> <pre><code>https://hztyjdgateway.2dmeeting.cn:3150</code></pre> <h5>对接准备数据示例</h5> <p>对接时由对接人员从平台查看给出,注意secret的保密!!!</p> <pre><code>//用户唯一key tenantCode:&amp;quot;hzkj&amp;quot; //用户唯一secret tenantSecret:&amp;quot;c593f09b-1091-4b90-8c37-efe0fea50c17&amp;quot;</code></pre> <h5>请求头示例</h5> <p>所有的请求都需要在请求头里面加入下面示例参数</p> <pre><code>&amp;quot;x-tenant-code&amp;quot;:&amp;quot;hzkj&amp;quot; &amp;quot;x-request-id&amp;quot;:&amp;quot;953f8952-781d-41e0-97a4-33b3eeb5c91e&amp;quot; &amp;quot;x-timestamp&amp;quot;:&amp;quot;1712658960231&amp;quot; &amp;quot;x-auth&amp;quot;:&amp;quot;通过下面提供的方法将提供的key和secret以及时间戳拼接后使用base64加密生成字符串&amp;quot;</code></pre> <h5>demo</h5> <pre><code>import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import java.io.IOException; import java.util.UUID; /** * TODO * * @Description * @Author WL * @Date 2024/8/26 **/ public class ClientDemo { public static void main(String[] args) { CloseableHttpClient httpclient = HttpClients.createDefault(); String ip = &amp;quot;https://hztyjdgateway.2dmeeting.cn:3150&amp;quot;; String url = &amp;quot;/subassembly/su/c/v1/test/v1&amp;quot;; url = ip + url; // 创建httppost HttpPost httppost = new HttpPost(url); try { String tenantCode = &amp;quot;hzkj&amp;quot;; Long timestamp = System.currentTimeMillis(); AuthClientUtil.ClientAuthData dd = new AuthClientUtil.ClientAuthData(); dd.setTenantCode(tenantCode); dd.setTimestamp(timestamp); String requestId = UUID.randomUUID().toString(); dd.setRequestId(requestId); String xauth = AuthClientUtil.encryptByClientSecret(dd, &amp;quot;eYwl8mutgCS+oAhtS3kz9uIZMeiQPTbPd+kBJ9GBY=&amp;quot;); httppost.setHeader(&amp;quot;x-tenant-code&amp;quot;, tenantCode); httppost.setHeader(&amp;quot;x-request-id&amp;quot;, requestId); httppost.setHeader(&amp;quot;x-timestamp&amp;quot;, String.valueOf(timestamp)); httppost.setHeader(&amp;quot;x-auth&amp;quot;, xauth); httppost.setHeader(&amp;quot;Content-Type&amp;quot;, &amp;quot;application/json&amp;quot;); String param = &amp;quot;582382930465157&amp;quot;; httppost.setEntity(new StringEntity(param, &amp;quot;UTF-8&amp;quot;)); CloseableHttpResponse response = httpclient.execute(httppost); String retMes = AuthClientUtil.getRetMes(response); System.out.println(&amp;quot;retMes:&amp;quot; + retMes); } catch (Exception e) { e.printStackTrace(); } finally { // 关闭连接,释放资源 try { httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } } } </code></pre> <h5>加密工具类</h5> <pre><code> import com.fasterxml.jackson.annotation.JsonValue; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.util.EntityUtils; import java.nio.charset.StandardCharsets; import java.util.Base64; /** * TODO * * @Description * @Author WL * @Date 2024/8/27 **/ public class AuthClientUtil { /** * 获取用于传输的{@link ClientAuthData}加密字符串 * * @param clientAuthData 加密原始数据,会先转换成|分隔字段的形式然后加密 * @param clientSecret 客户秘钥 * @return 加密字符串 */ public static String encryptByClientSecret(ClientAuthData clientAuthData, String clientSecret) { assertNotNull(clientAuthData, &amp;quot;clientAuthData&amp;quot;); assertNotBlank(clientAuthData.getTenantCode(), &amp;quot;clientAuthData.tenantCode&amp;quot;); assertNotBlank(clientAuthData.getRequestId(), &amp;quot;clientAuthData.requestId&amp;quot;); assertNotZeroOrNegative(clientAuthData.getTimestamp(), &amp;quot;clientAuthData.timestamp&amp;quot;); assertNotBlank(clientSecret, &amp;quot;clientSecret&amp;quot;); byte[] data = clientAuthData.toAuthPlainText().getBytes(StandardCharsets.UTF_8); byte[] secret = bytesBase64(clientSecret); byte[] cipher = aesEncrypt( EnumAesMode.ECB, EnumAesPadding.ZeroPadding, secret, null, data ); return stringBase64(cipher); } /** * 用于传输的Auth对象 */ public static class ClientAuthData { private String tenantCode; private long timestamp; private String requestId; public String getTenantCode() { return tenantCode; } public void setTenantCode(String tenantCode) { this.tenantCode = tenantCode; } public long getTimestamp() { return timestamp; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } public String getRequestId() { return requestId; } public void setRequestId(String requestId) { this.requestId = requestId; } /** * 转化成auth字符串 * * @return auth字符串 */ private String toAuthPlainText() { return format(&amp;quot;{}|{}|{}&amp;quot;, this.tenantCode, this.timestamp, this.requestId); } /** * 从auth字符串中读取对象 * * @param authPlainText auth字符串 * @return 读取后的新对象 */ public static ClientAuthData fromAuthPlainText(String authPlainText) { assertNotBlank(authPlainText, &amp;quot;authPlainText&amp;quot;); String[] arr = authPlainText.split(&amp;quot;\\|&amp;quot;); ClientAuthData data = new ClientAuthData(); data.setTenantCode(arr[0]); data.setTimestamp(Long.parseLong(arr[1])); data.setRequestId(arr[2]); return data; } } /** * 断言不能为空 */ public static void assertNotBlank(String str, String name) { if (str == null || &amp;quot;&amp;quot;.equals(str)) { throw new IllegalArgumentException(name + &amp;quot;不能为空&amp;quot;); } } /** * 获取base64编码的字符串 * * @param bytes 字节数组 * @return 字符串 */ public static String stringBase64(final byte[] bytes) { return Base64.getEncoder().encodeToString(bytes); } /** * 获取base64解码后的字节数组 * * @param cs 字符串 * @return 字节数组 */ public static byte[] bytesBase64(final CharSequence cs) { if (cs == null) { return null; } return Base64.getDecoder().decode(cs.toString()); } /** * aes加密 * * @param mode aes加密模式 * @param padding aes填充方式 * @param keyBytes 秘钥字节数组 * @param ivBytes 偏移向量 * @param plainDataBytes 明文字节数组 * @return 加密后的字节数组 */ public static byte[] aesEncrypt(EnumAesMode mode, EnumAesPadding padding, byte[] keyBytes, byte[] ivBytes, byte[] plainDataBytes) { cn.hutool.crypto.symmetric.AES aes = HutoolAesCreator.aes(mode, padding, keyBytes, ivBytes); return aes.encrypt(plainDataBytes); } /** * hutool工具中aes类的创建器 */ private static class HutoolAesCreator { public static cn.hutool.crypto.symmetric.AES aes(EnumAesMode mode, EnumAesPadding padding, byte[] keyBytes, byte[] ivBytes) { cn.hutool.crypto.symmetric.AES aes = new cn.hutool.crypto.symmetric.AES( getAesMode(mode), getAesPadding(padding), keyBytes, ivBytes ); return aes; } public static cn.hutool.crypto.symmetric.AES aesDefault(byte[] keyBytes) { return aes(EnumAesMode.ECB, EnumAesPadding.PKCS5Padding, keyBytes, null); } private static cn.hutool.crypto.Mode getAesMode(EnumAesMode mode) { switch (mode) { case NONE: return cn.hutool.crypto.Mode.NONE; case CBC: return cn.hutool.crypto.Mode.CBC; case CFB: return cn.hutool.crypto.Mode.CFB; case CTR: return cn.hutool.crypto.Mode.CTR; case CTS: return cn.hutool.crypto.Mode.CTS; case ECB: return cn.hutool.crypto.Mode.ECB; case OFB: return cn.hutool.crypto.Mode.OFB; case PCBC: return cn.hutool.crypto.Mode.PCBC; default: throw new RuntimeException(&amp;quot;不支持的AES算法模式&amp;quot;); } } private static cn.hutool.crypto.Padding getAesPadding(EnumAesPadding padding) { switch (padding) { case NoPadding: return cn.hutool.crypto.Padding.NoPadding; case ZeroPadding: return cn.hutool.crypto.Padding.ZeroPadding; case ISO10126Padding: return cn.hutool.crypto.Padding.ISO10126Padding; case OAEPPadding: return cn.hutool.crypto.Padding.OAEPPadding; case PKCS1Padding: return cn.hutool.crypto.Padding.PKCS1Padding; case PKCS5Padding: return cn.hutool.crypto.Padding.PKCS5Padding; case SSL3Padding: return cn.hutool.crypto.Padding.SSL3Padding; default: throw new RuntimeException(&amp;quot;不支持的AES填充方式&amp;quot;); } } } /** * AES算法模式 * * @Description * @Author WL * @Date 2024/4/11 **/ public enum EnumAesMode { /** * 无模式 */ NONE(0, &amp;quot;无模式&amp;quot;), /** * 密码分组连接模式(Cipher Block Chaining) */ CBC(1, &amp;quot;密码分组连接模式&amp;quot;), /** * 密文反馈模式(Cipher Feedback) */ CFB(2, &amp;quot;密文反馈模式&amp;quot;), /** * 计数器模式(A simplification of OFB) */ CTR(3, &amp;quot;计数器模式&amp;quot;), /** * Cipher Text Stealing */ CTS(4, &amp;quot;Cipher Text Stealing&amp;quot;), /** * 电子密码本模式(Electronic CodeBook) */ ECB(5, &amp;quot;电子密码本模式&amp;quot;), /** * 输出反馈模式(Output Feedback) */ OFB(6, &amp;quot;输出反馈模式&amp;quot;), /** * Propagating Cipher Block */ PCBC(7, &amp;quot;Propagating Cipher Block&amp;quot;), ; /** * 枚举值 */ private Integer value; /** * 枚举描述 */ private String desc; /** * 构造函数 */ EnumAesMode(Integer value, String desc) { this.value = value; this.desc = desc; } /** * 获取枚举描述 * * @return 枚举描述 */ public String getDesc() { return this.desc; } /** * 枚举数据库存储值、序列化json时取值 */ @JsonValue public Integer getValue() { return this.value; } } /** * AES算法填充方式 * * @Description * @Author WL * @Date 2024/4/11 **/ public enum EnumAesPadding { /** * 无补码 */ NoPadding(0, &amp;quot;无补码&amp;quot;), /** * 0补码,即不满block长度时使用0填充 */ ZeroPadding(1, &amp;quot;0补码&amp;quot;), ISO10126Padding(2, &amp;quot;ISO10126Padding&amp;quot;), OAEPPadding(3, &amp;quot;OAEPPadding&amp;quot;), PKCS1Padding(4, &amp;quot;PKCS1Padding&amp;quot;), PKCS5Padding(5, &amp;quot;PKCS5Padding&amp;quot;), SSL3Padding(6, &amp;quot;SSL3Padding&amp;quot;), ; /** * 枚举值 */ private Integer value; /** * 枚举描述 */ private String desc; /** * 构造函数 */ EnumAesPadding(Integer value, String desc) { this.value = value; this.desc = desc; } /** * 获取枚举描述 * * @return 枚举描述 */ public String getDesc() { return this.desc; } /** * 枚举数据库存储值、序列化json时取值 */ @JsonValue public Integer getValue() { return this.value; } } /** * 格式化文本, {} 表示占位符&amp;lt;br&amp;gt; * 此方法只是简单将占位符 {} 按照顺序替换为参数&amp;lt;br&amp;gt; * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可&amp;lt;br&amp;gt; * 例:&amp;lt;br&amp;gt; * 通常使用:format(&amp;quot;this is {} for {}&amp;quot;, &amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;) =》 this is a for b&amp;lt;br&amp;gt; * 转义{}: format(&amp;quot;this is \\{} for {}&amp;quot;, &amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;) =》 this is \{} for a&amp;lt;br&amp;gt; * 转义\: format(&amp;quot;this is \\\\{} for {}&amp;quot;, &amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;) =》 this is \a for b&amp;lt;br&amp;gt; * * @param template 模板,被替换的部分用 {} 表示 * @param params * @return */ public static String format(final String template, Object... params) { return cn.hutool.core.util.StrUtil.format(template, params); } /** * 断言不能为null */ public static void assertNotNull(Object obj, String name) { if (obj == null) { throw new IllegalArgumentException(name + &amp;quot;不能为null&amp;quot;); } } /** * 断言不能小于等于0 */ public static void assertNotZeroOrNegative(long n, String name) { if (n &amp;lt;= 0L) { throw new IllegalArgumentException(name + &amp;quot;不能为0或负数&amp;quot;); } } public static String getRetMes(CloseableHttpResponse response) throws Exception { String retMes = &amp;quot;&amp;quot;; try { HttpEntity entity = response.getEntity(); if (entity != null) { retMes = EntityUtils.toString(entity, &amp;quot;UTF-8&amp;quot;); } } finally { response.close(); } return retMes; } } </code></pre>

页面列表

ITEM_HTML