组件接口鉴权说明文档
<p>[TOC]</p>
<h5>接口对接域名</h5>
<p>>测试环境</p>
<pre><code>https://hztyjdgateway.2dmeeting.cn:3150</code></pre>
<h5>对接准备数据示例</h5>
<p>对接时由对接人员从平台查看给出,注意secret的保密!!!</p>
<pre><code>//用户唯一key
tenantCode:&quot;hzkj&quot;
//用户唯一secret
tenantSecret:&quot;c593f09b-1091-4b90-8c37-efe0fea50c17&quot;</code></pre>
<h5>请求头示例</h5>
<p>所有的请求都需要在请求头里面加入下面示例参数</p>
<pre><code>&quot;x-tenant-code&quot;:&quot;hzkj&quot;
&quot;x-request-id&quot;:&quot;953f8952-781d-41e0-97a4-33b3eeb5c91e&quot;
&quot;x-timestamp&quot;:&quot;1712658960231&quot;
&quot;x-auth&quot;:&quot;通过下面提供的方法将提供的key和secret以及时间戳拼接后使用base64加密生成字符串&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 = &quot;https://hztyjdgateway.2dmeeting.cn:3150&quot;;
String url = &quot;/subassembly/su/c/v1/test/v1&quot;;
url = ip + url;
// 创建httppost
HttpPost httppost = new HttpPost(url);
try {
String tenantCode = &quot;hzkj&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, &quot;eYwl8mutgCS+oAhtS3kz9uIZMeiQPTbPd+kBJ9GBY=&quot;);
httppost.setHeader(&quot;x-tenant-code&quot;, tenantCode);
httppost.setHeader(&quot;x-request-id&quot;, requestId);
httppost.setHeader(&quot;x-timestamp&quot;, String.valueOf(timestamp));
httppost.setHeader(&quot;x-auth&quot;, xauth);
httppost.setHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);
String param = &quot;582382930465157&quot;;
httppost.setEntity(new StringEntity(param, &quot;UTF-8&quot;));
CloseableHttpResponse response = httpclient.execute(httppost);
String retMes = AuthClientUtil.getRetMes(response);
System.out.println(&quot;retMes:&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, &quot;clientAuthData&quot;);
assertNotBlank(clientAuthData.getTenantCode(), &quot;clientAuthData.tenantCode&quot;);
assertNotBlank(clientAuthData.getRequestId(), &quot;clientAuthData.requestId&quot;);
assertNotZeroOrNegative(clientAuthData.getTimestamp(), &quot;clientAuthData.timestamp&quot;);
assertNotBlank(clientSecret, &quot;clientSecret&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(&quot;{}|{}|{}&quot;, this.tenantCode, this.timestamp, this.requestId);
}
/**
* 从auth字符串中读取对象
*
* @param authPlainText auth字符串
* @return 读取后的新对象
*/
public static ClientAuthData fromAuthPlainText(String authPlainText) {
assertNotBlank(authPlainText, &quot;authPlainText&quot;);
String[] arr = authPlainText.split(&quot;\\|&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 || &quot;&quot;.equals(str)) {
throw new IllegalArgumentException(name + &quot;不能为空&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(&quot;不支持的AES算法模式&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(&quot;不支持的AES填充方式&quot;);
}
}
}
/**
* AES算法模式
*
* @Description
* @Author WL
* @Date 2024/4/11
**/
public enum EnumAesMode {
/**
* 无模式
*/
NONE(0, &quot;无模式&quot;),
/**
* 密码分组连接模式(Cipher Block Chaining)
*/
CBC(1, &quot;密码分组连接模式&quot;),
/**
* 密文反馈模式(Cipher Feedback)
*/
CFB(2, &quot;密文反馈模式&quot;),
/**
* 计数器模式(A simplification of OFB)
*/
CTR(3, &quot;计数器模式&quot;),
/**
* Cipher Text Stealing
*/
CTS(4, &quot;Cipher Text Stealing&quot;),
/**
* 电子密码本模式(Electronic CodeBook)
*/
ECB(5, &quot;电子密码本模式&quot;),
/**
* 输出反馈模式(Output Feedback)
*/
OFB(6, &quot;输出反馈模式&quot;),
/**
* Propagating Cipher Block
*/
PCBC(7, &quot;Propagating Cipher Block&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, &quot;无补码&quot;),
/**
* 0补码,即不满block长度时使用0填充
*/
ZeroPadding(1, &quot;0补码&quot;),
ISO10126Padding(2, &quot;ISO10126Padding&quot;),
OAEPPadding(3, &quot;OAEPPadding&quot;),
PKCS1Padding(4, &quot;PKCS1Padding&quot;),
PKCS5Padding(5, &quot;PKCS5Padding&quot;),
SSL3Padding(6, &quot;SSL3Padding&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;
}
}
/**
* 格式化文本, {} 表示占位符&lt;br&gt;
* 此方法只是简单将占位符 {} 按照顺序替换为参数&lt;br&gt;
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可&lt;br&gt;
* 例:&lt;br&gt;
* 通常使用:format(&quot;this is {} for {}&quot;, &quot;a&quot;, &quot;b&quot;) =》 this is a for b&lt;br&gt;
* 转义{}: format(&quot;this is \\{} for {}&quot;, &quot;a&quot;, &quot;b&quot;) =》 this is \{} for a&lt;br&gt;
* 转义\: format(&quot;this is \\\\{} for {}&quot;, &quot;a&quot;, &quot;b&quot;) =》 this is \a for b&lt;br&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 + &quot;不能为null&quot;);
}
}
/**
* 断言不能小于等于0
*/
public static void assertNotZeroOrNegative(long n, String name) {
if (n &lt;= 0L) {
throw new IllegalArgumentException(name + &quot;不能为0或负数&quot;);
}
}
public static String getRetMes(CloseableHttpResponse response) throws Exception {
String retMes = &quot;&quot;;
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
retMes = EntityUtils.toString(entity, &quot;UTF-8&quot;);
}
} finally {
response.close();
}
return retMes;
}
}
</code></pre>