【问题标题】:What's wrong with Spring's @RequestBody byte[] (length bigger than expected)?Spring 的 @RequestBody byte[] 有什么问题(长度大于预期)?
【发布时间】:2014-02-17 01:39:33
【问题描述】:

我尝试使用@RequestBody byte[] data 作为自动 Base64 解码器,以便接收一些 RSA 加密数据,然后在我的服务中解密:

控制器:

@RequestMapping
void doIt(@RequestBody byte[] data) {
    service.doIt(data);
}

服务:

void doIt(byte[] encryptedData) {               
    String testDataString = "hgLGtzF4D8tlJbVivQgaXXwfI9TbQ//PrYHEez5k93sqJSi17eLCa+r/cGNGvoxDRaPYONvP4yxW0ajKDFrusQ4V4owfhkijS9KzBOTjBeAVmA+5jBsZCdoxwCA65DiP5lJ+GRbn8CjcjCr4DaEWFbWHsyvY4NGGAQLuYv+PyZipfU9pXTEDsBb15NwaHlD5m7Z4CHWdWTt1ARvRaQs56Bp63/IEmGR7w4brA1+iuKPv83FLh0rsxyoJ+F8TeqtuPhm2fHTh1FiHn0Bpaqqoyd/cBl0/utSzu4qoZhB3AiVgLjnT6Iy9p5nVoAozxQo/Es59LrpGZfjYJer073jNIg==";
    byte[] testDataBytes = Base64.decodeBase64(testDataString);

    System.out.println(encryptedData.length);
    System.out.println(testDataBytes.length);

    System.out.println(new String(encryptedData).length());
    System.out.println(Base64.encodeBase64String(testDataBytes).length());

    System.out.println(new String(encryptedData).equals(Base64.encodeBase64String(testDataBytes)));
}

请求:

Content-Type: application/octet-stream

Request Payload: 
hgLGtzF4D8tlJbVivQgaXXwfI9TbQ//PrYHEez5k93sqJSi17eLCa+r/cGNGvoxDRaPYONvP4yxW0ajKDFrusQ4V4owfhkijS9KzBOTjBeAVmA+5jBsZCdoxwCA65DiP5lJ+GRbn8CjcjCr4DaEWFbWHsyvY4NGGAQLuYv+PyZipfU9pXTEDsBb15NwaHlD5m7Z4CHWdWTt1ARvRaQs56Bp63/IEmGR7w4brA1+iuKPv83FLh0rsxyoJ+F8TeqtuPhm2fHTh1FiHn0Bpaqqoyd/cBl0/utSzu4qoZhB3AiVgLjnT6Iy9p5nVoAozxQo/Es59LrpGZfjYJer073jNIg==

输出:

(Spring) Byte length:          344
(test)   Byte length:          256
(Spring) Base64 String length: 344
(test)   Base64 String length: 344
         Base64 String equals: true

如你所见:

  • 请求中包含的数据与我放入源代码中的测试数据完全相同。

  • 进行 Base64 编码后,两者的长度相同,并且“相等”

  • 但是:字节数组的长度不同。

问题:

为什么Spring生成的byte[]更大? (尝试使用一些 RSA 私钥对其进行解码时没有用,因为 RSA 期望长度为 256。)

解决方法:

控制器:

@RequestMapping
void doIt(@RequestBody String data) {
    service.doIt(Base64.decodeBase64(data));
}

【问题讨论】:

  • 我无法理解你的问题。您提供的输出与您显示的测试代码无关,并且此测试代码对我没有任何意义:例如new String(encryptedData) 没有任何意义。
  • 我的测试代码中有 5 个System.out.println(...)。输出中有 5 个值(344256344344true)。使用new String(byte[]),您可以将字节数组转换为字符串。 Java 文档说:通过使用平台的默认字符集解码指定的字节数组来构造一个新的 {@code String}。。我只是用它来澄清问题。 ...我尝试描述的问题是:Spring 的 HttpMessageConverter 构造了一个比它应该更大的字节数组。如果你使用的东西依赖于每一位(比如 RSA 解密),它根本就不起作用。
  • 但是字节数组不包含使用默认平台编码编码为字节的字符。它包含不代表有效字符的任意二进制字节。无论如何,您假设 Spring base64 解码请求主体以将其绑定到字节数组。事实并非如此。它按原样读取请求中的字节流,而不进行任何解码。
  • 但它确实解码了它。如我的代码示例所示,我得到了@RequestBody byte[] data,然后我只需执行new String(data)。如果我使用System.out.println(new String(data)),我可以在我的请求中看到我用作有效负载的相同字符串。所以它以某种方式起作用。甚至equals 方法也返回true
  • 所以,你发送一个 base64 字符串,Spring 将它读取为一个字节数组,然后你从这个字节数组构造一个字符串,并看到你发送的 base64 字符串,对吧?如果是这样,那显然意味着 Spring 不会解码任何内容。如果它被解码,您将能够获取一个字节数组(例如包含 1、2、3、4、5),base-64 对其进行编码(这将产生字符串“AQIDBAU =”,在请求中发送它,并将 [1, 2, 3, 4, 5] 视为字节数组)。

标签: spring spring-mvc base64 bytearray converter


【解决方案1】:

在 Spring 的 ByteArrayHttpMessageConverter 中,您可以找到 readInternal 方法,它将请求的内容转换为所需的格式(在本例中为 byte[])。

代码(Spring 4.0.0.RELEASE)

long contentLength = inputMessage.getHeaders().getContentLength();
ByteArrayOutputStream bos = new ByteArrayOutputStream(contentLength >= 0 ? (int) contentLength : StreamUtils.BUFFER_SIZE);
StreamUtils.copy(inputMessage.getBody(), bos);
return bos.toByteArray();

第二行说:*如果请求有效负载不为空,则取其内容长度,并以此设置ByteArrayOutputStream 的容量。否则,如果内容长度

我不太确定为什么要以这种方式实现,但它实际上是这样工作的。

【讨论】:

  • 这与它无关。作者遇到的问题是,他以某种方式假设 Spring 将解码他的 344 个字符长的 BASE64 编码字符串,但 Spring 没有这样做,它只是为每个字符创建一个 1 字节的字节数组。
  • 它是这样实现的,因为没有内容类型标头它不知道要分配多少内存,所以它分配 4K。如果您知道大小,请指定内容长度
猜你喜欢
  • 1970-01-01
  • 2011-09-01
  • 2017-12-27
  • 1970-01-01
  • 2021-02-01
  • 2018-03-23
  • 2015-06-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多