三方对接接口数据安全问题

文档说明

小记一下在与第三方对接过程的一些操作

对接流程

在这里插入图片描述

数据传输

每次请求均会对请求来源以及数据的合法性进行校验,采取以下策略,之后接口明细中会说明采用哪种加密方式

获取 Token

请求地址: POST https:xxxx
接口说明:获取调用凭据,有效期 24h、获取后之后调用接口添加到请求头 Authorization

请求参数
字段数据类型是否必传说明示例
app_idString客户端IDthirdpartner
app_secretString客户端密钥@m2!2q15^#0d&@
响应参数
字段说明示例
code响应状态码200
msg响应描述信息请求成功
data响应体eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQ…

常规数据加密

常规数据加密,每次请求数据添加随机字符串 once_str 和时间戳 timestamp 并通过 MD5 对全部数据进行加密、接收方对其进行校验证,示例如下

public class EncryptSign {

    public static final String APP_ID 	  = "thirdpartner";
    public static final String APP_SECRET = "@m*2!2q1*5^#0d&@";

    // 生成签名
    public static String createSign(SortedMap<String, String> params) {
        return params.keySet().stream()
                .sorted()
                .map(k -> k + "=" + params.get(k) + "&")
                .reduce((x, y) -> x + y)
                .map(d -> d.substring(0, d.length() - 1))
                .map(d -> d.concat(APP_SECRET))
                .map(EncryptSign::encode)
                .map(String::toUpperCase)
                .get();
    }

    public static boolean verifySign(HttpServletRequest request) {
        Map<String, String[]> params = request.getParameterMap();
        SortedMap<String, String> map = new TreeMap<>();
        String expSign = null;
        for (Map.Entry<String, String[]> pv : params.entrySet()) {
            String param = pv.getKey();
            String[] value = pv.getValue();
            if (!param.equals("sign")) {
                map.put(param, value[0]);
            } else {
                expSign = value[0];
            }
        }
        return expSign.equals(createSign(map));
    }

    public static String generateOnceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static String encode(String value) {
        StringBuilder sb = new StringBuilder();
        try {
            MessageDigest md = MessageDigest.getInstance(MD5);
            byte[] bs = value.getBytes();
            byte[] mb = md.digest(bs);
            for (int i = 0; i < mb.length; i++) {
                int v = mb[i] & 0xFF;
                if (v < 16) {
                    sb.append("0");
                }
                sb.append(Integer.toHexString(v));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        //创建随机字符串和时间戳
        String once_str = generateOnceStr();
        String timestamp = String.valueOf(System.currentTimeMillis());
        //测试数据集
        SortedMap<String, String> params = new TreeMap<>();
        params.put("param1", "a");
        params.put("param2", "b");
        params.put("once_str", once_str);
        params.put("timestamp", timestamp);
        //常规数据加密
        params.put("sign", createSign(params));
        System.out.println(params);
    }
}

调用示例
Map<String, String> map = new HashMap<>();
// 业务参数
map.put("app_id", "xxxx");
map.put("app_secret", "xxxx");
// 公共参数
map.put("once_str", EncryptSign.generateOnceStr());
map.put("timestamp", String.valueOf(System.currentTimeMillis()));
// MD5 加密
map.put("sign", EncryptSign.createSign(map));
// Http 调用
String url = "http://xxx/xx/xx";
String result = HttpClientUtil.httpPost(url, map);
System.out.println(result);

敏感数据加密

涉及敏感数据,采用 AES 进行加解密,注意妥善保管密钥,示例如下

public class EncryptAES {

    private static final String secret    = "@5^22&%c*9^283*@";
    private static final String algorithm = "AES/ECB/PKCS5Padding";

    //加密
    public static String encrypt(String content) {
        try {
            Security.addProvider(new SunJCE());
            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(ENCRYPT_MODE, new SecretKeySpec(secret.getBytes(), AES));
            return Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes("UTF-8")));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //解密
    public static String decrypt(String encrypt) {
        try {
            Security.addProvider(new SunJCE());
            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(DECRYPT_MODE, new SecretKeySpec(secret.getBytes("UTF-8"), AES));
            return new String(cipher.doFinal(Base64.getDecoder().decode(encrypt)));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        //创建随机字符串和时间戳
        String once_str = EncryptSign.generateOnceStr();
        String timestamp = String.valueOf(System.currentTimeMillis());
        //测试数据集
        SortedMap<String, String> params = new TreeMap<>();
        params.put("param1", "a");
        params.put("param2", "b");
        params.put("once_str", once_str);
        params.put("timestamp", timestamp);

        String content = JSON.toJSONString(params);
        System.out.println("明文: " + content);
        System.out.println("加密: " + encrypt(content));
        System.out.println("解密: " + decrypt(encrypt(content)));
    }
}
调用示例
Map<String, String> map = new HashMap<>();
// 业务参数
map.put("app_id", "xxxx");
map.put("app_secret", "xxxx");
// 公共参数
map.put("once_str", EncryptSign.generateOnceStr());
map.put("timestamp", String.valueOf(System.currentTimeMillis()));
// AES加密参数封装到 sign 字段
Map<String, String> param = new HashMap<>();
param.put("sign", EncryptAES.encrypt(toJSONString(map));
// Http 调用
String url = "http://xxx/xx/xx";
String result = HttpClientUtil.httpPost(url, param);
System.out.println(result);
### 对接三方快递柜 HTTP API 接口 #### 一、准备工作 为了成功对接三方快递柜的HTTP API接口,需完成如下准备事项: - **获取API凭证**:联系快递柜提供商以获得必要的认证信息,如API密钥或令牌。这些凭证用于身份验证和授权访问。 - **阅读官方文档**:仔细研读供应商提供的API文档[^3],了解支持的功能列表、请求URL、参数说明以及响应格式等内容。确保理解每项功能的具体实现细节及其约束条件。 - **设置开发环境**:确认本地编程工具已安装并配置好相应的库/框架,以便能够发起网络请求并与服务器交互数据。对于大多数Web服务来说,建议采用`UTF-8`字符集编码发送POST表单数据,例如使用`application/x-www-form-urlencoded;charset=utf-8`作为Content-Type头部值。 #### 二、构建请求体结构 基于所选语言编写客户端程序时,应遵循以下模式来组装向远程端点提交的数据包: ```json { "method": "string", // 请求的方法名 "param": { /* 参数对象 */ }, "sign": "string" // 数字签名字符串 } ``` 其中,“method”代表调用的服务名称;“param”内含实际传递给后台处理的各项属性;最后通过特定算法计算得出的安全散列码赋值于“sign”。 #### 三、示例代码片段 下面给出一段Python脚本示范如何利用requests模块执行上述流程中的某一步骤——查询订单状态: ```python import requests from hashlib import md5 def generate_sign(params_dict, secret_key): """生成MD5加密后的签名""" sorted_items = sorted(params_dict.items()) query_string = ''.join([f"{k}{v}" for k, v in sorted_items]) + secret_key m = md5() m.update(query_string.encode('utf-8')) return m.hexdigest() url = 'https://api.example.com/order/query' params = {'order_id': '12345'} secret = 'your_secret_here' headers = {"Content-Type": "application/json"} data = { "method": "OrderQuery", "param": params, "sign": generate_sign(params, secret), } response = requests.post(url=url, json=data, headers=headers).json() if response['success']: print(f'Order status is {response["result"]["status"]}') else: print(response.get('message', 'Failed to get order info.')) ``` 此段代码展示了怎样构造一个合法有效的JSON负载,并将其封装到HTTPS POST请求之中去检索指定ID对应包裹的状态详情。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值