记一次对请求头为multipartform-data的post请求参数加密操作以及传参

本文详细介绍了一次针对multipart/form-data格式的POST请求参数进行RSA加密及Base64转码的过程,包括秘钥加密代码、Base64转码实现、MD5签名加密,以及如何处理multipart/form-data请求头的参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记一次对请求头为multipart/form-data的post请求参数加密操作以及传参

背景:对接一个第三方系统,提供的接口要求对参数进行RSA加密,Base64转码。而且请求头为multipart/form-data。

1、秘钥加密代码

/**
     * <p>
     * 私钥加密
     * </p>
     *
     * @param data
     *            源数据
     * @param privateKey
     *            私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        return decryptOrEncrypt(data, keyFactory, priKey, Cipher.ENCRYPT_MODE, MAX_ENCRYPT_BLOCK);
    }
/**
     * 对数据进行解密/加密过程
     *
     * @param data       已加密数据
     * @param keyFactory key工厂
     * @param key        key(pub/pri)
     * @param model      加解密模式
     * @return
     * @throws Exception
     */
    private static byte[] decryptOrEncrypt(byte[] data, KeyFactory keyFactory, Key key, int model, int block) throws Exception {
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(model, key);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > block) {
                cache = cipher.doFinal(data, offSet, block);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * block;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

2、Base64转码

使用的是org.apache.commons.codec.binary包下的Base64

public static String encodeBase64String(byte[] binaryData) {
        return StringUtils.newStringUsAscii(encodeBase64(binaryData, false));
    }

3、签名MD5加密

/**
*@param str1 加密方式
*@param str2 需加密字符串
*/
private static String digest(String str1, String str2) {
		try {
			byte[] Data = str2.getBytes();
			MessageDigest Cdigest = MessageDigest.getInstance(str1);
			Cdigest.update(Data);
			// MD5直 --dig
			byte[] dig = Cdigest.digest();
			// PrintByteArray("dig", dig);
			// byte []形式表达的MD5 转化为 16H形式String表达的MD5
			String str16hMD5 = bytesToHexString(dig);
			return str16hMD5;
		} catch (Exception e) {
			return null;
		}
	}

4、请求头为multipart/form-data,参数需要特殊处理

a.获取连接对象
/**
     * 获得连接对象
     *
     * @param urlStr
     * @param headers
     * @return
     * @throws IOException
     */
    private static HttpURLConnection getHttpURLConnection(String urlStr, Map<String, Object> headers) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //设置超时时间
        conn.setConnectTimeout(50000);
        conn.setReadTimeout(50000);
        //允许输入流
        conn.setDoInput(true);
        //允许输出流
        conn.setDoOutput(true);
        //不允许使用缓存
        conn.setUseCaches(false);
        //请求方式
        conn.setRequestMethod("POST");
        //设置编码 utf-8
        conn.setRequestProperty("Charset", "UTF-8");
        //设置为长连接
        conn.setRequestProperty("connection", "keep-alive");

        //设置其他自定义 headers
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, Object> header : headers.entrySet()) {
                conn.setRequestProperty(header.getKey(), header.getValue().toString());
            }
        }

        return conn;
    }

b、写文件类型的表单参数

/**
     * 写文件类型的表单参数
     *
     * @param paramName 参数名
     * @param filePath  文件路径
     * @param boundary  分隔符
     * @param out
     * @throws IOException
     */
    private static void writeFile(String paramName, String filePath, String boundary,
                                  DataOutputStream out) {
        try (BufferedReader fileReader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)))) {
            /**
             * 写分隔符--${boundary},并回车换行
             */
            String boundaryStr = BOUNDARY_PREFIX + boundary + LINE_END;
            out.write(boundaryStr.getBytes());
            /**
             * 写描述信息(文件名设置为上传文件的文件名):
             * 写 Content-Disposition: form-data; name="参数名"; filename="文件名",并回车换行
             * 写 Content-Type: application/octet-stream,并两个回车换行
             */
            String fileName = new File(filePath).getName();
            String contentDispositionStr = String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"", paramName, fileName) + LINE_END;
            out.write(contentDispositionStr.getBytes());
            String contentType = "Content-Type: application/octet-stream" + LINE_END + LINE_END;
            out.write(contentType.getBytes());

            String line;
            while ((line = fileReader.readLine()) != null) {
                out.write(line.getBytes());
            }
            //回车换行
            out.write(LINE_END.getBytes());
        } catch (Exception e) {
            LOGGER.error("写文件类型的表单参数异常", e);
        }
    }

c、写普通的表单参数

/**
 * 写普通的表单参数
 *
 * @param boundary 分隔符
 * @param out
 * @param entry    参数的键值对
 * @throws IOException
 */
private static void writeSimpleFormField(String boundary, DataOutputStream out, Map.Entry<String, Object> entry) throws IOException {
    //写分隔符--${boundary},并回车换行
    String boundaryStr = BOUNDARY_PREFIX + boundary + LINE_END;
    out.write(boundaryStr.getBytes());
    //写描述信息:Content-Disposition: form-data; name="参数名",并两个回车换行
    String contentDispositionStr = String.format("Content-Disposition: form-data; name=\"%s\"", entry.getKey()) + LINE_END + LINE_END;
    out.write(contentDispositionStr.getBytes());
    //写具体内容:参数值,并回车换行
    String valueStr = entry.getValue().toString() + LINE_END;
    out.write(valueStr.getBytes());
}

d、请求

/**
     *
     * @param url 请求路径
     * @param filePathMap 文件参数
     * @param keyValues 普通参数
     * @return 请求结果
     */
    public static String postFormData(String url,Map<String,String> filePathMap,Map<String,Object> keyValues)  {
        LOGGER.info("发起http请求的url:{}",url);
        LOGGER.info("文件请求参数filePathMap:{}",filePathMap);
        LOGGER.info("文本参数keyValues:{}",keyValues);
        StringBuffer buffer = new StringBuffer();
        HttpURLConnection con = null;
        try {
            con = getHttpURLConnection(url, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //分隔符,可以任意设置,这里设置为 MyBoundary+ 时间戳(尽量复杂点,避免和正文重复)
        String boundary = "MyBoundary" + System.currentTimeMillis();
        //设置 Content-Type 为 multipart/form-data; boundary=${boundary}
        con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

        //发送参数数据
        try (DataOutputStream out = new DataOutputStream(con.getOutputStream())) {
            //发送普通参数
            if (keyValues != null && !keyValues.isEmpty()) {
                for (Map.Entry<String, Object> entry : keyValues.entrySet()) {
                    writeSimpleFormField(boundary, out, entry);
                }
            }
            //发送文件类型参数
            if (filePathMap != null && !filePathMap.isEmpty()) {
                for (Map.Entry<String, String> filePath : filePathMap.entrySet()) {
                    writeFile(filePath.getKey(), filePath.getValue(), boundary, out);
                }
            }
            //写结尾的分隔符--${boundary}--,然后回车换行
            String endStr = BOUNDARY_PREFIX + boundary + BOUNDARY_PREFIX + LINE_END;
            out.write(endStr.getBytes());
            out.flush();
            out.close();
            if (con.getResponseCode() == 200) {
                InputStream in = con.getInputStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(in, "utf-8"));
                String temp;
                while ((temp = br.readLine()) != null) {
                    buffer.append(temp);
                    // buffer.append("\n");
                }
                in.close();
                br.close();
            }
        } catch (Exception e) {
            LOGGER.error("HttpUtils.postFormData 请求异常!", e);
        }
        LOGGER.info("http请求相应数据:{}", buffer.toString());
        return buffer.toString();
    }

5、根据业务场景调用方法

 public String enrollAdd(EnrollAddVO vo){
        Map map = new HashMap(4);
        JSONObject requestObj = new JSONObject();
        requestObj.put("name",vo.getName());
        requestObj.put("sex",vo.getSex());
        byte[] requestString = null;
        try {
            requestString = RsaUtils.encryptByPrivateKey(requestObj.toJSONString().getBytes("UTF-8"),RAS_KEY);
        }catch (Exception e){
            log.info("RSA加密失败:{}",e.getMessage());
        }
        String base64Str = Base64.encodeBase64String(requestString);
        String sign = MD5Util.genMD5(base64Str + SECRET_KEY);
        map.put("appKey",APP_KEY);
        map.put("request",base64Str);
        map.put("sign",sign);
        return HttpClientUtil.postFormData(ENROLL_ADD_URL,null,map);
    }
<think>好的,用户之前问了关于Vue2中跨组件通信的问题,现在又转向了axios的get和post方法。我需要先回顾之前的对话,确保这次的回答与之前的建议一致,或者是否有需要衔接的地方。 首先,用户可能是在开发Vue2项目时遇到了前后端数据交互的问题,所以才会询问axios的使用方法。之前的问题是关于组件间通信,现在转向了HTTP请求,这可能意味着他们在处理前端获取数据后需要与不同组件交互,或者是在提交表单数据时遇到问题。 接下来,用户的问题是关于axios.get和post的区别和使用方法。我需要详细解释这两种HTTP方法的用途、语法、参数传递方式以及常见的使用场景。同时,考虑到用户可能已经了解一些Vue的知识,可以结合Vue的示例代码来说明如何在组件中使用axios。 需要确认用户是否了解RESTful API的基本概念,是否需要简要介绍GET和POST在HTTP协议中的角色。另外,可能需要提到axios的配置选项,比如设置请求头、处理响应和错误等,尤其是POST请求中常见的Content-Type,比如application/json和表单数据的区别。 用户可能有隐藏的需求,比如如何处理跨域请求、如何传递参数(URL参数 vs 请求体),或者如何在Vue项目中全局配置axios实例。这些都可以在回答中适当提及,但不要太过深入,以免偏离当前问题。 另外,考虑到用户之前使用的是Vue2,需要注意在示例代码中使用的是Vue2的语法,比如methods选项和生命周期钩子的使用。同时,确保示例中的axios用法与Vue2兼容,比如在created或mounted钩子中调用axios方法。 最后,总结GET和POST的主要区别,以及在实际开发中的选择建议,帮助用户根据不同的需求选择合适的方法。同时,提醒他们注意安全性问题,比如敏感数据不应通过GET传递,以及POST请求的数据加密等。</think>在 Vue.js 或其他前端项目中,`axios.get()` 和 `axios.post()` 是用于发送 HTTP 请求的两个核心方法,分别对应 **GET** 和 **POST** 两种请求方法。以下是它们的详细对比和使用指南: --- ### **一、axios.get()** #### **用途** - 用于向服务器 **请求数据**(查询操作- 数据通过 URL 参数传递(明文可见) #### **语法** ```javascript axios.get(url [, config]) .then(response => { /* 处理成功响应 */ }) .catch(error => { /* 处理错误 */ }); ``` #### **参数传递方式** 1. **URL 路径参数** 动态参数直接拼接到 URL 中: ```javascript axios.get('/api/user/123') // 获取 ID 为 123 的用户 ``` 2. **URL 查询参数** 通过 `params` 对象传递: ```javascript axios.get('/api/users', { params: { page: 2, limit: 10 } }); // 实际 URL:/api/users?page=2&limit=10 ``` #### **典型场景** - 获取商品列表 - 查询用户信息 - 搜索过滤数据 --- ### **二、axios.post()** #### **用途** - 用于向服务器 **提交数据**(创建/修改操作- 数据通过请求体(body)传递(相对更安全) #### **语法** ```javascript axios.post(url, data [, config]) .then(response => { /* 处理成功响应 */ }) .catch(error => { /* 处理错误 */ }); ``` #### **参数传递方式** 1. **请求传参** 直接传递对象(默认 `Content-Type: application/json`): ```javascript axios.post('/api/login', { username: 'admin', password: '123456' }); ``` 2. **表单数据传参** 使用 `FormData`(适合文件上传): ```javascript const formData = new FormData(); formData.append('file', file); // 上传文件 formData.append('comment', '示例文本'); axios.post('/api/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }); ``` #### **典型场景** - 用户登录/注册 - 表单提交 - 文件上传 - 创建新资源(如新增订单) --- ### **三、核心区别** | 特性 | `axios.get()` | `axios.post()` | |---------------------|--------------------------------|----------------------------------| | **HTTP 方法** | GET | POST | | **数据位置** | URL 参数 | 请求体(body) | | **数据可见性** | 明文暴露在地址栏 | 隐藏在请求体中 | | **安全性** | 较低(不适合敏感数据) | 较高(适合敏感数据) | | **缓存** | 可能被浏览器缓存 | 默认不缓存 | | **幂等性** | 幂等(多次请求结果相同) | 非幂等(可能产生副作用) | | **数据长度限制** | 受 URL 长度限制(约 2KB) | 无限制(受服务器配置影响) | --- ### **四、完整示例(Vue 2 项目)** #### **GET 请求示例** ```javascript export default { methods: { fetchUserList() { axios.get('/api/users', { params: { role: 'admin', status: 1 } }) .then(response => { this.users = response.data; }) .catch(error => { console.error('请求失败:', error); }); } } } ``` #### **POST 请求示例** ```javascript export default { data() { return { form: { title: '', content: '' } }; }, methods: { submitArticle() { axios.post('/api/articles', this.form) .then(response => { alert('提交成功!ID: ' + response.data.id); }) .catch(error => { alert('提交失败: ' + error.message); }); } } } ``` --- ### **五、关键注意事项** 1. **GET 请求的安全限制** - 不要用 GET 传递密码、Token 等敏感信息 - 避免用 GET 实现删除/修改操作(违反 RESTful 规范) 2. **POST 请求的编码格式** - 默认使用 `application/json`,需确保后端支持解析 - 传统表单提交需设置 `Content-Type: application/x-www-form-urlencoded` 3. **跨域请求处理** - 配置后端 CORS 或使用代理(如 Vue 的 `devServer.proxy`) 4. **全局配置**(推荐) ```javascript // main.js 中设置全局默认值 axios.defaults.baseURL = 'https://api.example.com'; axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; ``` --- ### **六、常见问题解决** #### **Q1:POST 请求后端接收不到参数?** - 检查请求头 `Content-Type` 是否与后端匹配 - 使用 `URLSearchParams` 处理表单编码: ```javascript const params = new URLSearchParams(); params.append('key1', 'value1'); axios.post('/api', params); ``` #### **Q2:如何取消重复请求?** - 使用 `CancelToken`: ```javascript const source = axios.CancelToken.source(); axios.get('/api', { cancelToken: source.token }); // 取消请求 source.cancel('用户取消操作'); ``` --- 通过合理选择 GET 和 POST 方法,可以更规范地实现前后端数据交互。对于复杂项目,建议结合 Vuex 或 Pinia 管理请求状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值