import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.malk.rjk.Boot; import com.malk.rjk.server.RjkServer; import com.malk.server.common.McException; import com.smecloud.apigw.client.ApigwClient; import com.smecloud.apigw.model.ApiRequest; import com.smecloud.apigw.model.ApiResult; import com.smecloud.apigw.model.ApigwConfig; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; 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 org.apache.http.util.EntityUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.*; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.*; import static com.smecloud.apigw.constant.HttpMethod.GET; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest(classes = Boot.class) public class test { @Test public void testService() throws IOException, NoSuchAlgorithmException, InvalidKeyException { Map mes_params = push_app_authorize("300657", "9163551ef2e2a2cd2398dc05ec527132", "356919829745242112");//根据应用ID(Client ID),第三方实例ID调取实时 if (mes_params.size() > 0) { String appSecret = mes_params.get("appSecret").toString(); String domain = mes_params.get("domain").toString(); if (domain != "" && appSecret != "" && domain != null && appSecret != null) { get_material("300657", domain, appSecret, "9163551ef2e2a2cd2398dc05ec527132"); } } // // CookieStore cookieStore = new BasicCookieStore(); // // 创建一个 Httpclient 实例 // CloseableHttpClient httpclient = HttpClients.custom().setDefaultCookieStore(cookieStore).build(); // HttpGet otherRequest = new HttpGet("https://api.kingdee.com/jdy/v2/bd/material"); // otherRequest.setHeader("Content-Type", "300657"); // otherRequest.setHeader("X-Api-ClientID", "application/json"); // otherRequest.setHeader("X-Api-Auth-Version", "2.0"); // otherRequest.setHeader("X-Api-TimeStamp", String.valueOf(System.currentTimeMillis())); // otherRequest.setHeader("X-Api-SignHeaders", "X-Api-TimeStamp,X-Api-Nonce"); // otherRequest.setHeader("X-Api-Nonce", this.get_randomNumber()); // otherRequest.setHeader("X-Api-Signature", get_app_signature("tcyhmh71","206bfff1ea61c4699f55ce895848c3e52a8dff02")); // otherRequest.setHeader("app-token", this.get_appToken()); // otherRequest.setHeader("X-GW-Router-Addr", this.get_appToken()); // CloseableHttpResponse otherResponse = httpclient.execute(otherRequest); } /** * 主动获取授权 * * @return */ public static Map push_app_authorize(String clientId, String Secret, String outerInstanceId) throws IOException, NoSuchAlgorithmException, InvalidKeyException { Map mes_params = new TreeMap<>(); String url = "https://api.kingdee.com/jdyconnector/app_management/push_app_authorize?outerInstanceId=" + outerInstanceId; HttpPost httpPost = new HttpPost(url); String TimeStamp = String.valueOf(System.currentTimeMillis()); String randomNumber = get_randomNumber(); /** * Signature加密 */ String QQFS = "POST"; String path = "/jdyconnector/app_management/push_app_authorize"; Map params = new TreeMap<>(); // TreeMap 会自动按键的 ASCII 码排序 params.put("outerInstanceId", outerInstanceId); String Signature = get_app_signature(Secret, QQFS, path, params, TimeStamp, randomNumber);//X-Api-Signature加密获取 httpPost.setHeader("X-Api-ClientID", clientId); httpPost.setHeader("X-Api-Auth-Version", "2.0"); httpPost.setHeader("X-Api-TimeStamp", TimeStamp); httpPost.setHeader("X-Api-Nonce", randomNumber); httpPost.setHeader("X-Api-SignHeaders", "X-Api-TimeStamp,X-Api-Nonce"); httpPost.setHeader("X-Api-Signature", Signature); // 设置请求体(如果有) httpPost.setEntity(new StringEntity("", "UTF-8")); try (CloseableHttpClient httpClient = HttpClients.createDefault()) { // 发送请求并处理响应 HttpResponse response = httpClient.execute(httpPost); String responseBody = EntityUtils.toString(response.getEntity()); JSONObject jsonObject = JSON.parseObject(responseBody); if (jsonObject.getString("code").equals("200")) { JSONArray dataArray = jsonObject.getJSONArray("data"); // 假设JSON数组中只有一个对象,或者我们只关心第一个对象 JSONObject dataObject = dataArray.getJSONObject(0); String appSecret = dataObject.getString("appSecret"); String domain = dataObject.getString("domain"); mes_params.put("appSecret", appSecret); mes_params.put("domain", domain); } else { } } catch (Exception e) { e.printStackTrace(); } return mes_params; } /** * 获取商品列表 * * @return */ public static String get_material(String clientId, String domain, String appSecret, String Secret) throws IOException, NoSuchAlgorithmException, InvalidKeyException { String url = "https://api.kingdee.com/jdy/v2/bd/material"; HttpGet httpGet = new HttpGet(url); String TimeStamp = String.valueOf(System.currentTimeMillis()); String randomNumber = get_randomNumber(); /** * Signature加密 */ String QQFS = "GET"; String path = "/jdy/v2/bd/material"; Map params = new TreeMap<>(); // TreeMap 会自动按键的 ASCII 码排序 String Signature = get_app_signature(Secret, QQFS, path, params, TimeStamp, randomNumber);//X-Api-Signature加密获取 httpGet.setHeader("X-Api-ClientID", clientId); httpGet.setHeader("X-Api-Auth-Version", "2.0"); httpGet.setHeader("X-Api-TimeStamp", TimeStamp); httpGet.setHeader("X-Api-Nonce", randomNumber); httpGet.setHeader("X-Api-SignHeaders", "X-Api-TimeStamp,X-Api-Nonce"); httpGet.setHeader("X-Api-Signature", Signature); httpGet.setHeader("Content-Type", "application/json"); httpGet.setHeader("app-token", get_appToken(appSecret)); httpGet.setHeader("X-GW-Router-Addr", domain); try (CloseableHttpClient httpClient = HttpClients.createDefault()) { // 发送请求并处理响应 HttpResponse response = httpClient.execute(httpGet); String responseBody = EntityUtils.toString(response.getEntity()); JSONObject jsonObject = JSON.parseObject(responseBody); System.out.println("jsonObject:" + jsonObject); String errcode = jsonObject.getString("errcode"); if (errcode.equals("0")) {//判断有返回值 JSONObject dataObject = jsonObject.getJSONObject("data"); // 从data对象中获取rows数组 JSONArray rowsArray = dataObject.getJSONArray("rows"); // 循环遍历rows数组中的每个对象 for (int i = 0; i < rowsArray.size(); i++) { JSONObject rowObject = rowsArray.getJSONObject(i); // 获取rowObject中的任意字段,例如name String shop_name = rowObject.getString("name");//商品名称 String shop_number = rowObject.getString("number");//商品编码 if (!shop_number.equals("")){ } // 打印或其他处理 System.out.println("商品名称: " + shop_name); //根据编号查询详情 // 你可以继续获取其他字段并进行处理 } } else { } } catch (McException e) { System.out.println("e:"+e.getMessage()); } return ""; } //正整数 public static String get_randomNumber() { Random random = new Random(); // 指定随机数的范围(例如,1到100之间的随机正整数) int min = 100000000; int max = 999999999; // 生成随机数 int randomNumber = random.nextInt((max - min) + 1) + min; return String.valueOf(randomNumber); } public static String get_appToken(String appSecret) throws IOException { ApigwConfig config = new ApigwConfig(); //设置client_id config.setClientID("300657"); //设置client_secret config.setClientSecret("9163551ef2e2a2cd2398dc05ec527132"); ApigwClient apigwClient = ApigwClient.getInstance(); //初始化API网关客户端 apigwClient.init(config); ApiRequest request = new ApiRequest(GET, "api.kingdee.com", "/jdyconnector/app_management/kingdee_auth_token"); Map map = new HashMap<>(); map.put("app_key", "tcyhmh71"); String app_signature = get_app_signature("tcyhmh71", appSecret); map.put("app_signature", app_signature); request.setQuerys(map); request.setBodyJson(JSONObject.toJSONString("").getBytes()); ApiResult result = ApigwClient.getInstance().send(request); // 解析JSON字符串 String jsonBody = result.getBody(); // 解析JSON字符串 JSONObject jsonObject = JSON.parseObject(jsonBody); JSONObject dataObject = jsonObject.getJSONObject("data"); // 获取app-token,注意fastjson对key中包含特殊字符(如-)的处理是自动的 String appToken = dataObject.getString("app-token"); System.out.println(appToken); return appToken; } //加密X-Api-Signature加密规则 public static String get_app_signature(String app_key, String appSecret) { try { // 创建一个Mac实例,并指定使用HmacSHA256算法 Mac mac = Mac.getInstance("HmacSHA256"); // 使用appSecret作为密钥 SecretKeySpec secretKeySpec = new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); mac.init(secretKeySpec); // 对appKey进行加密 byte[] hmacBytes = mac.doFinal(app_key.getBytes(StandardCharsets.UTF_8)); // 将加密结果转换为16进制字符串 StringBuilder hexString = new StringBuilder(2 * hmacBytes.length); for (byte b : hmacBytes) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) hexString.append('0'); hexString.append(hex); } // 将16进制字符串进行Base64编码 byte[] hexBytes = hexString.toString().getBytes(StandardCharsets.UTF_8); String base64Encoded = Base64.getEncoder().encodeToString(hexBytes); return base64Encoded; } catch (NoSuchAlgorithmException | InvalidKeyException e) { throw new RuntimeException("Error while generating HMAC-SHA256 hash", e); } } //加密X-Api-Signature生成规则 public static String get_app_signature(String clientSecret, String QQFS, String path, Map params, String TimeStamp, String randomNumber) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException { String method = QQFS; String originalString = path; String target = "/"; // 要替换的字符,转换为字符串 String replacement = "%2F"; // 替换后的字符串 // 使用String的replace方法替换所有出现的target为replacement String encodedString = originalString.replace(target, replacement); StringBuilder signatureText = new StringBuilder(); signatureText.append(method).append("\n"); signatureText.append(encodedString).append("\n"); // 拼接 params 参数(进行两次 URL 编码并转换为大写,但 TreeMap 已排序且只需编码一次) if (params != null) { Map encodedParams = doubleUrlEncodeParams(params); // 按参数名的ASCII码升序排序 List> sortedParams = new ArrayList<>(encodedParams.entrySet()); sortedParams.sort(Comparator.comparing(Map.Entry::getKey)); String signString = generateSignString(sortedParams); signatureText.append(signString).append("\n"); System.out.println("签名字符串: " + signString); } else { signatureText.append("\n"); } // 添加 headers 参数(x-api-nonce 和 x-api-timestamp,小写编码但拼接时保持原样) // 注意:这里实际上不需要再次编码,因为 nonce 和 timestamp 通常是数字或基64字符串,且规则要求小写(但编码后是大写字母的情况在这里不适用) signatureText.append("x-api-nonce:").append(randomNumber).append("\n"); signatureText.append("x-api-timestamp:").append(TimeStamp).append("\n"); // 输出签名原文(实际签名算法应在此原文基础上进行) System.out.println("Signature Text:\n" + signatureText.toString()); String message = signatureText.toString(); String hmacHex = get_app_signature(message, clientSecret); return hmacHex; } /** * 对参数值进行两次URL编码,并将字母转为大写 */ private static Map doubleUrlEncodeParams(Map params) { Map encodedParams = new HashMap<>(); for (Map.Entry entry : params.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); try { // 第一次URL编码 String firstEncode = URLEncoder.encode(value, StandardCharsets.UTF_8.toString()); // 第二次URL编码 String secondEncode = URLEncoder.encode(firstEncode, StandardCharsets.UTF_8.toString()); // 转为大写 encodedParams.put(key, secondEncode.toUpperCase()); } catch (UnsupportedEncodingException e) { throw new RuntimeException("URL编码失败", e); } } return encodedParams; } /** * 生成签名字符串(key1=value1&key2=value2) */ private static String generateSignString(List> sortedParams) { StringBuilder signString = new StringBuilder(); for (Map.Entry entry : sortedParams) { if (signString.length() > 0) { signString.append("&"); } signString.append(entry.getKey()).append("=").append(entry.getValue()); } return signString.toString(); } public static String signMessage(String message, String clientSecret) throws NoSuchAlgorithmException, InvalidKeyException { // 创建一个HMAC-SHA256 Mac实例 Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); // 将clientSecret转换为密钥规范 SecretKeySpec secretKeySpec = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); // 初始化Mac实例 sha256_HMAC.init(secretKeySpec); // 计算HMAC byte[] hmacBytes = sha256_HMAC.doFinal(message.getBytes(StandardCharsets.UTF_8)); // 将字节数组转换为十六进制字符串 return bytesToHex(hmacBytes); } /** * 将字节数组转换为十六进制字符串 * * @param bytes 要转换的字节数组 * @return 转换后的十六进制字符串 */ public static String bytesToHex(byte[] bytes) { Formatter formatter = new Formatter(); for (byte b : bytes) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } @Autowired private RjkServer rjkServer; @Test public void testH3UserToweiling() { // 准备测试数据 Map testData = new HashMap<>(); // 替换为实际的氚云业务对象ID // testData.put("BizObjectId", "0233a0df-24a1-4ee2-9de8-b4f3029b5594"); // testData.put("BizObjectId", "d47b5b5b-0a4b-4120-8d08-6c5d79f8378f"); // testData.put("BizObjectId", "151452f7-33f9-407e-a95e-5e040168ba3d"); testData.put("BizObjectId", "967a68c1-5197-47cd-86e1-0a1cd42afb1f"); // 调用H3UserToweiling方法 rjkServer.H3UserToweiling(testData); } @Test public void testH3BusinessToWeiling() { // 准备测试数据 Map testData = new HashMap<>(); // 替换为实际的氚云业务对象ID // testData.put("BizObjectId", "b7932c25-b081-498a-9c7e-da67c14208c2"); testData.put("BizObjectId", "1f610bab-ada8-4247-a55c-d4d389450ce9"); // 调用H3UserToweiling方法 rjkServer.H3BusinessToWeiling(testData); } }