【问题标题】:URL encoding using the new Spring UriComponentsBuilder使用新的 Spring UriComponentsBuilder 进行 URL 编码
【发布时间】:2013-08-08 23:38:32
【问题描述】:

我正在尝试使用 spring 的 UriComponentsBuilder 生成一些用于 oauth 交互的 url。查询参数包括回调url和参数值等实体,其中包含空格。

尝试使用 UriComponentBuilder(因为 UriUtils 现在已弃用)

UriComponentsBuilder urlBuilder = UriComponentsBuilder.fromHttpUrl(oauthURL);
urlBuilder.queryParam("client_id", clientId);
urlBuilder.queryParam("redirect_uri", redirectURI);
urlBuilder.queryParam("scope", "test1 test2");

String url = urlBuilder.build(false).encode().toUriString();

不幸的是,虽然 scope 参数中的空格被成功替换为 '+',但 redirect_uri 参数根本没有进行 url 编码。

例如,

redirect_uri=https://oauth2-login-demo.appspot.com/code

应该结束了

redirect_uri=https%3A%2F%2Foauth2-login-demo.appspot.com%2Fcode

但未受影响。深入研究代码,特别是 org.springframework.web.util.HierarchicalUriComponents.Type.QUERY_PARAM.isAllowed(c) :

if ('=' == c || '+' == c || '&' == c) {
  return false;
}
else {
  return isPchar(c) || '/' == c || '?' == c;
}

显然允许使用 ':' 和 '/' 字符,而口香糖不应该这样做。它一定是在做一些其他类型的编码,虽然对于我的生活,我无法想象是什么。我在这里叫错树了吗?

谢谢

【问题讨论】:

    标签: spring


    【解决方案1】:

    UriComponentsBuilder 正在根据RFC 3986 对您的 URI 进行编码,其中关于 URI 的“查询”组件的第 3.4 节特别值得注意。

    在“查询”组件中,字符“/”和“:”是允许的,不需要转义。

    以 '/' 字符为例:'query' 组件(由未转义的 '?' 和(可选)'#' 字符明确分隔)不是分层的,并且 '/' 字符没有特殊意义。所以不需要编码。

    【讨论】:

    • 这是不正确的,因为&和其他有意义的字符也没有被转义。 UriComponentsBuilder 没有对查询参数进行 url 编码。
    • @Adam Millerchip,我不明白 - 当然 & is 正在逃脱。 OP粘贴了代码sn-p sn-p,表明不允许&,也不允许=或+。所有这些都会被逃脱。
    • 你会这么认为,但事实并非如此。试试看。
    • 我试过它确实逃脱了&和=。鉴于 OP 中的代码 sn-p ,不知道为什么 + 没有被转义。我怀疑我最初的推理成立:+ 符号在查询片段中没有任何意义。 UriComponentsBuilder urlBuilder = UriComponentsBuilder.fromHttpUrl("example.org"); urlBuilder.queryParam("scope", "test1&test2=test3+test4"); String url = urlBuilder.build(false).encode().toUriString(); System. out.println(url); 结果:http://example.org?scope=test1%26test2%3Dtest3+test4
    • 在我报告了 toUriString() 行为后,如果没有变量存在,spring 开发人员立即修复它以使用 build().encode().toUriString() (原始行为)。现在像“{}”这样的查询参数将被正确编码。 jira.spring.io/browse/SPR-17630
    【解决方案2】:

    据我了解,UriComponentsBuilder 不会自动对查询参数进行编码,只是对它实例化的原始 HttpUrl 进行编码。换句话说,你仍然需要显式编码:

    String redirectURI= "https://oauth2-login-demo.appspot.com/code";
    urlBuilder.queryParam("redirect_uri", URLEncoder.encode(redirectURI,"UTF-8" ));
    

    【讨论】:

    • 嗯...'encode' 方法声明:使用其特定的编码规则对所有 URI 组件进行编码,并将结果作为新的 {@code UriComponents} 实例返回。这似乎意味着它会进行 URL 编码。似乎将其留给“类型”(在本例中为 Type.QUERY_PARAM)来决定要编码哪些字符。所以它会编码一些字符..但不是一些非常重要的字符。如果不对 URL 编码的查询参数进行编码,encode 方法会做什么?
    • 它对您传递给它的 URL 进行编码,但不是每个查询参数
    • 嗯.. 这不是很有用。这很奇怪,因为它是一个 URL 构建器,您添加查询参数,然后构建然后编码。我会假设它正在为我建立一个安全的 URL。如果查询参数没有使用 URL 编码,那么添加查询参数有什么意义?
    • 我同意,并且会像您那样假设。也许设计师有一些理由不隐式编码参数,但也可能没有
    • UriComponentsBuilder 包含一个 encode() 方法,我认为它将对 url 和查询参数进行编码。
    【解决方案3】:

    尝试扫描 UriComponentsBuilder 文档,有一个名为 build(boolean encoded) 的方法

    示例代码 1:

    UriComponents uriComponents = UriComponentsBuilder.fromPath("/path1/path2").build(true);
    

    这是我的示例代码 2:

    UriComponents uriComponents = UriComponentsBuilder.newInstance()
                .scheme("https")
                .host("my.host")
                .path("/path1/path2").query(parameters).build(true);
    
    URI uri= uriComponents.toUri();
    
    ResponseEntity<MyEntityResponse> responseEntity = restTemplate.exchange(uri,
                HttpMethod.GET, entity, typeRef);
    

    【讨论】:

    • .build(true) 单独不会逃脱.queryParam,它只是说你所有的参数都已经逃脱了。感觉没用,因为它没有进行任何转义......可能在 Spring 方面正在进行中
    • @jediz 如果您的 url 已经编码并且不希望再次对其进行验证和编码,这很有用。也许他们应该将其从 encoded 重命名为 alreadyEncoded
    【解决方案4】:

    我尝试了上述所有解决方案,直到我成功为止。

    在我的示例中,我尝试对 ZonedDateTime 格式 2022-01-21T10:17:10.228+06:00 进行编码。加号是个问题。

    解决我的问题的方法是手动编码值 + 使用 URI 而不是字符串值(两者都非常重要)。

    之前:

    restTemplate.exchange(
      UriComponentsBuilder
        .queryParam("fromDateTime", "2022-01-21T10:17:10.228+06:00")
        .build()
        .toUriString(),
      HttpMethod.GET,
      null,
      new ParameterizedTypeReference<List<MyDto>>() {}
    );
    

    之后:

    restTemplate.exchange(
      UriComponentsBuilder
        .queryParam("fromDateTime", URLEncoder.encode("2022-01-21T10:17:10.228+06:00", StandardCharsets.UTF_8))
        .build(true)
        .toUri(),
      HttpMethod.GET,
      null,
      new ParameterizedTypeReference<List<MyDto>>() {}
    );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-09
      • 1970-01-01
      • 2011-04-03
      • 1970-01-01
      • 1970-01-01
      • 2014-05-27
      • 1970-01-01
      相关资源
      最近更新 更多