Spring Boot REST Sping Boot 控制器中请求头的编码

nr9pn0ug  于 2023-11-17  发布在  Spring
关注(0)|答案(1)|浏览(235)

**TLDR;

如果应用程序的默认字符集是UTF-8,那么在调用控制器方法时,某些UTF-8头会被编码两次。在spring MVC中,编码在哪里进行,如何控制?

详情

我们在REST控制器中使用头值时会遇到以下问题
我们使用一个SSO服务作为代理,并在请求中注入一些额外的头。注入的值是mail,givenname,lastname等。
代理在header中注入UTF-8编码的值(我们已经使用tcpdump和wireshark进行了检查)

  1. GET /api/v1/foo/bar HTTP/1.1
  2. Host: blabla.fr
  3. ....
  4. Content-Type: application/json;charset=UTF-8
  5. Accept: */*
  6. Accept-Encoding: gzip, deflate, sdch, br
  7. Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
  8. ...
  9. givenName: Gérard

字符串
我们的控制器就像这个

  1. @RestController
  2. @RequestMapping("/api/v1/foo")
  3. public class FooController {
  4. @RequestMapping("/bar")
  5. public ResponseEntity<List<String>> bar(@RequestHeader("foo") String foo, @RequestHeader("givenname") String firstname) throws UnsupportedEncodingException {
  6. ArrayList<String> list = Lists.newArrayList(
  7. "FOO: " + foo,
  8. "CHARSET: " + Charset.defaultCharset().name(),
  9. "FOO WITH DEFAULT CHARSET: " + asHexList(foo),
  10. "FOO WITH UTF8: " + asHexList(new String(foo.getBytes("UTF-8"))),
  11. "INJECTED HEADER WITH DEFAULT CHARSET: " + asHexList(firstname),
  12. "INJECTED HEADER WITH UTF8: " + asHexList(new String(firstname.getBytes("UTF-8")))
  13. );
  14. return ResponseEntity.ok( list );
  15. }
  16. }
  17. /**
  18. * Print a concatenated list of hexa représentation of the string's bytes
  19. * assert asHexList(new String("é", "UTF-8"))equals("c3 e9")
  20. * @param String s
  21. * @return String
  22. */
  23. public String asHexList(String s) {...}


我们观察到以下行为
当系统的LANG为fr_FR.ISO-8859-1时,则返回为

  1. [
  2. "FOO: Gérard",
  3. "CHARSET: ISO-8859-1",
  4. "FOO WITH DEFAULT CHARSET: 47 e9 72 61 72 64",
  5. "FOO WITH UTF8: 47 c3 a9 72 61 72 64",
  6. "INJECTED HEADER WITH DEFAULT CHARSET: 47 c3 a9 72 61 72 64",
  7. "INJECTED HEADER WITH UTF8: 47 c3 83 c2 a9 72 61 72 64"
  8. ]


当LANG为fr_FR.UTF-8时,我们有

  1. [
  2. "FOO: Gérard",
  3. "CHARSET: UTF-8",
  4. "FOO WITH DEFAULT CHARSET: 47 c3 a9 72 61 72 64",
  5. "FOO WITH UTF8: 47 c3 a9 72 61 72 64",
  6. "INJECTED HEADER WITH DEFAULT CHARSET: 47 c3 83 c2 a9 72 61 72 64",
  7. "INJECTED HEADER WITH UTF8: 47 c3 83 c2 a9 72 61 72 64"
  8. ]


我们可以看到

  • 当应用程序的字符集为“ISO-8859-1”时,自定义头('foo')以“ISO-8859-1”(é=>e9)编码,注入头以“UTF-8”(é=>c3 a9)编码
  • 当应用程序的字符集为“UTF-8”时,自定义头('foo')以“UTF-8”(é=>c3 a9)编码,注入头以“UTF-8”TWICE(é=>c3 a9=>c3 83 c2 a9)编码

Spring MVC中编码在哪里处理,如何控制?Spring中LANG、file. encoding和default charset之间有哪些关系?
我们在1.3.3.RELEASE spring Boot 中嵌入了spring-web 4.2.5-RELEASE。
我们非常欢迎你的帮助。

cczfrluj

cczfrluj1#

我假设你使用的是tomcatweb服务器,那么看看ByteChunk类,它实际上将头文件从String刷新为默认ISO-8859-1字符集的字节。

  1. /**
  2. * Default encoding used to convert to strings. It should be UTF8, as most
  3. * standards seem to converge, but the servlet API requires 8859_1, and this
  4. * object is used mostly for servlets.
  5. */
  6. public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;

字符串
更糟糕的是,Spring Mvc不提供任何机会让我们指定正确的字符集来编码/解码头值。
最后,我通过将UTF-8字符串字节提前解码为ISO-8859-1来修复了这个问题,这样以后ByteChunk就可以正确编码了。

  1. headers.set(HttpHeaders.CONTENT_DISPOSITION,
  2. new String(("attachment; filename=" + attachment.filename).getBytes(StandardCharsets.UTF_8),
  3. StandardCharsets.ISO_8859_1));

展开查看全部

相关问题