场景说明:
近期在开发一款小程序需要上传图片,于是按照阿里云官方的文档的说明进行编写(https://help.aliyun.com/document_detail/31989.html#title-neu-ft5-rlp),
服务端签名后在客户端直传,传输完成后由OSS进行回调。
后续需要验证OSS签名才允许执行接口,使用了阿里云官方提供的Java示例代码测试,但一直无法通过验证。

问题发现

在对应方法进行打断点调试后,发现通过流读取的ossCallbackBody一直为空,没有body信息,验证自然无法通过。但是没有找到解决办法

服务端签名直传并设置上传回调spring boot 服务端签名验证失败_spring

解决问题

后续看到这篇博文:阿里云Oss服务器端签名上传遇到的问题—回调签名校验失败,

按文中提供的方法将回调的类型从application/x-www-form-urlencoded

变更为application/json后,检验就正常通过了。

但是这篇博文中没有说明真正原因,于是自己试着找了一下

服务端签名直传并设置上传回调spring boot 服务端签名验证失败_request_02

问题溯源

  1. 打断点进入body读取方法,发现读到的数据为-1
  2. 发现携带数据的是InputStream,由请求传入的HttpServletRequest.getInputStream()获得,搜索此方法的相关信息,得知流读取完成后就不能被再次读取,返回-1,符合(1)的场景,说明流已经被读取过了
  3. 后来找到博文和帖子的3楼

根据Servlet规范,如果同时满足下列条件,则请求体(Entity)中的表单数据,将被填充到request的parameter集合中(request.getParameter系列方法可以读取相关数据):
1 这是一个HTTP/HTTPS请求
2 请求方法是POST(querystring无论是否POST都将被设置到parameter中)
3 请求的类型(Content-Type头)是application/x-www-form-urlencoded
4 Servlet调用了getParameter系列方法
如果上述条件没有同时满足,则相关的表单数据不会被设置进request的parameter集合中,相关的数据可以通过request.getInputStream()来访问。反之,如果上述条件均满足,相关的表单数据将不能再通过request.getInputStream()来读取。

今天遇到一个Controller请求经过Spring MVC 的RequestMapping处理后,只能通过request.getParameter()获取到参数、无法通过request.getInputStream()和request.getReader()读取内容,很可能就是因为在请求经过Spring MVC时已调用过request.getParameter()方法的原因

当请求体内容是其它类型时,比如 multipart/form-data或application/json时,无法通过request.getParameter()获取到请求内容,此时只能通过request.getInputStream()和request.getReader()方法获取请求内容,此时调用request.getParameter()也不会影响第一次调用request.getInputStream()或request.getReader()获取到请求内容。

  1. 按文中意思结合实际情况,HttpServletRequest已经被Spring框架通过RequestMapping处理,填充到request的parameter集合中,于是getInputStream()失效,导致返回-1,需要通过getParameter()读取相关数据

分析后获得两种解决方法:
a.将回调请求类型改为application/json
b.将阿里云Java示例的GetPostBody方法进行改造,通过getParameter()获得数据