Spring RestTemplate:使用multipart/form-data获取403禁止访问,但使用curl正常工作。

huangapple go评论78阅读模式
英文:

Spring RestTemplate: getting 403 forbidden for multipart/form-data but working with curl

问题

以下是翻译后的内容:

我正在使用 Spring 的 RestTemplate 来进行一个 POST 请求,请求中包含一个文件和一个字符串参数。以下是代码示例:

HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer ...");
httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);

File file = new File("src/main/resources/test.json");
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
byte[] fileBytes = toByteArray(in);

MultiValueMap<String, String> fileMap = new LinkedMultiValueMap<>();
ContentDisposition contentDisposition = ContentDisposition
        .builder("form-data")
        .name("file")
        .filename("test.json")
        .build();
fileMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
fileMap.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
org.springframework.http.HttpEntity<byte[]> fileEntity =
        new org.springframework.http.HttpEntity<>(fileBytes, fileMap);

MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("stringFormInput", "xyz");
body.add("file", fileEntity);
org.springframework.http.HttpEntity<MultiValueMap<String, Object>> entity =
        new org.springframework.http.HttpEntity<>(body, httpHeaders);

ResponseEntity<JsonNode> responseEntity =
        restTemplate.postForEntity("https://uploadFile", entity, JsonNode.class);

但我得到了一个 403 FORBIDDEN 的响应。

然而,如果我使用下面的 curl 命令,请求可以成功执行:

curl --location --request POST 'https://uploadFile' \
--header 'Content-Type: multipart/form-data' \
--header 'Authorization: Bearer ...' \
--form 'file=@/D:/resources/test.json' \
--form 'stringFormInput=xyz'

我还尝试使用 httpClient,但依然得到 403 错误,没有任何错误信息。

HttpPost post = new HttpPost("https://uploadFile");
post.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE);
post.setHeader(HttpHeaders.AUTHORIZATION, "Bearer ...");

File file = new File("src/main/resources/test.json");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addBinaryBody("file", file, ContentType.DEFAULT_BINARY, "test.json");
builder.addTextBody("stringFormInput", "xyz", ContentType.DEFAULT_BINARY);
HttpEntity entity = builder.build();

post.setEntity(entity);
HttpClient httpClient = HttpClientBuilder.create().build();
HttpResponse response = httpClient.execute(post);

编辑

目前,我通过在 Java 中执行 curl 命令来绕过了这个问题:

File file = new File("src/main/resources/test.json");
String[] command = {"curl", "--location",
        "--request", "POST", "https://uploadFile",
        "--header", "Content-Type: multipart/form-data",
        "--header", "Authorization: Bearer ...",
        "-F", "file=@" + file.getPath(),
        "--form", "stringFormInput=xyz"};

ProcessBuilder builder = new ProcessBuilder(command);
builder.redirectErrorStream(true);

String curlResult = "";
String line = "";

try {
    Process process = builder.start();
    BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()));
    while (true) {
        line = r.readLine();
        if (line == null) {
            break;
        }
        curlResult = curlResult + line;
    }
} catch (Exception e) {
    e.printStackTrace();
}
英文:

I am using spring rest-template to POST request form-data of 1 file and 1 string parameter, here is code:

    HttpHeaders headers = new HttpHeaders();
    headers.set(&quot;Authorization&quot;, &quot;Bearer ...&quot;);
    httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
    File file=new File(&quot;src\main\resources\test.json&quot;);
    BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
    byte[] fileBytes = toByteArray(in);

    MultiValueMap&lt;String, String&gt; fileMap = new LinkedMultiValueMap&lt;&gt;();
    ContentDisposition contentDisposition = ContentDisposition
            .builder(&quot;form-data&quot;)
            .name(&quot;file&quot;)
            .filename(&quot;test.json&quot;)
            .build();
    fileMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
    fileMap.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
    org.springframework.http.HttpEntity&lt;byte[]&gt; fileEntity =
            new org.springframework.http.HttpEntity&lt;&gt;(fileBytes, fileMap);

    MultiValueMap&lt; String, Object &gt; body = new LinkedMultiValueMap&lt;&gt;();
    body.add(&quot;stringFormInput&quot;, &quot;xyz&quot;);
    body.add(&quot;file&quot;, fileEntity);
    org.springframework.http.HttpEntity&lt; MultiValueMap&lt; String, Object &gt; &gt; entity =
            new org.springframework.http.HttpEntity&lt;&gt;(body, httpHeaders);

    ResponseEntity&lt; JsonNode &gt; responseEntity =
            restTemplate.postForEntity(&quot;https://uploadFile&quot;, entity, JsonNode.class);

But I'm getting 403 FORBIDDEN response

However, If I use curl command below, it executes successfully

curl --location --request POST &#39;https://uploadFile&#39; \
--header &#39;Content-Type: multipart/form-data&#39; \
--header &#39;Authorization: Bearer ...&#39; \
--form &#39;file=@/D:/resources/test.json&#39; \
--form &#39;stringFormInput=xyz&#39; \

I also tried with httpClient, still, it is 403 with no any cause message

HttpPost post = new HttpPost(&quot;https://uploadFile&quot;);
post.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE);
post.setHeader(HttpHeaders.AUTHORIZATION, &quot;Bearer ...&quot;);
    
File file=new File(&quot;src\main\resources\test.json&quot;);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addBinaryBody(&quot;file&quot;, file, ContentType.DEFAULT_BINARY, &quot;test.json&quot;);
builder.addTextBody(&quot;stringFormInput&quot;, &quot;xyz&quot;, ContentType.DEFAULT_BINARY);
HttpEntity entity = builder.build();

post.setEntity(entity);
HttpClient httpClient = HttpClientBuilder.create().build();
HttpResponse response = httpClient.execute(post);

EDIT

For now I did a workaround by executing curl from JAVA

    File file=new File(&quot;src\main\resources\test.json&quot;);
    String[] command = {&quot;curl&quot;, &quot;--location&quot;,
            &quot;--request&quot;, &quot;POST&quot;, &quot;https://uploadFile&quot;,
            &quot;--header&quot;, &quot;Content-Type: multipart/form-data&quot;,
            &quot;--header&quot;, &quot;Authorization: Bearer ...&quot;,
            &quot;-F&quot;, &quot;file=&quot; + &quot;@&quot;+file.getPath(),
            &quot;--form&quot;, &quot;stringFormInput=xyz&quot;};

    ProcessBuilder builder = new ProcessBuilder(command);
    builder.redirectErrorStream(true);

    String curlResult = &quot;&quot;;
    String line = &quot;&quot;;

    try {
        Process process = builder.start();
        BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()));
        while (true) {
            line = r.readLine();
            if (line == null) {
                break;
            }
            curlResult = curlResult + line;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

答案1

得分: 1

我遇到了类似的问题,cURL 能够正常执行,但 Spring 代码却返回 403 错误。
问题出在接收该请求的目标位置。一些 Web 服务器,如 Nginx,默认情况下需要强制性的头部信息,比如 "User-Agent",这个可以被禁用。

Spring 默认情况下不会传递额外的头部信息。如果目标使用了 Nginx,尝试添加 "User-Agent" 头部,或者查找他们使用了哪种 Web 服务器。

以下是我用于参考的代码。

  1. 这段代码返回 403 错误。

    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);

  2. 这段代码运行正常。

    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
    httpHeaders.add("User-Agent", "PostmanRuntime/7.26.3");

英文:

So I faced a similar problem that cURL was executing properly but Spring code was giving 403.
The problem lies with the target which is receiving this request. Few web servers like Nginx required mandatory headers like "User-Agent" by default which can be disabled.

By default, Spring doesn't pass any extra header. Try adding "User-Agent" if the target is using Ngnix or find what web server they are using.

Sharing my code for reference.

  1. This code gave 403

    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);

  2. this code worked fine.

    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
    httpHeaders.add("User-Agent", "PostmanRuntime/7.26.3");

huangapple
  • 本文由 发表于 2020年5月5日 20:51:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/61613617.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定