URI 链接 (URI Links)
Spring Framework 提供了多种工具来处理 URI 的构建、解析和编码,主要围绕 UriComponentsBuilder 展开。
UriComponentsBuilder
这是构建 URI 的核心类,支持变量填充、查询参数添加以及 URL 编码。
java
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode() // 对模板和变量进行编码
.buildAndExpand("Westin", "123") // 填充变量
.toUri();
// 结果: https://example.com/hotels/Westin?q=123kotlin
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123") // 简写方式,隐式包含 encode核心功能
1. 相对请求构建 (ServletUriComponentsBuilder)
可以基于当前 HTTP 请求构建相对的 URI。这在重定向或返回 Location 标头时非常有用。
java
// 复用当前请求的 scheme, host, port 和 path 并在后面追加
URI uri = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(id)
.toUri();2. 控制器链接 (MvcUriComponentsBuilder)
直接从控制器的类名或方法引用构建 URI,避免硬编码字符串路径。这在构建 HATEOAS 风格的 API 时非常强大。
java
// 基于方法引用构建 URL
URI uri = MvcUriComponentsBuilder
.fromMethodCall(on(BookingController.class).getBooking(21L))
.buildAndExpand(42)
.toUri();3. URI 解析 (Parsing)
Spring 支持两种解析器模式:
- RFC 解析器:严格遵守 RFC 3986 规范(默认)。
- WhatWG 解析器:类似于浏览器的宽容解析模式,能够处理一些不规范的输入。可以通过自定义
UriBuilderFactory来切换。
4. URI 编码
UriComponentsBuilder 支持两种模式:
encode(): 在扩展变量之前对模板编码(推荐,能正确处理变量中的保留字符)。UriComponents.encode(): 在扩展变量之后对最终组件编码。
补充教学
1. 安全警示:防止 Open Redirect 攻击
在使用 UriComponentsBuilder 处理用户提供的 URL 时要极其小心。攻击者可能会传入类似 //evil.com 的值。 建议:始终验证 Host 是否在白名单内,或者只使用 fromPath 构建基于本站的相对路径。
2. XSRF 与 URI 链接
虽然 URI 工具类只负责构建字符串,但在 HATEOAS 或 RESTful API 中,返回给前端的链接如果包含敏感操作,务必确保这些链接只能通过 POST 访问且带有 CSRF Token。
3. 选择合适的编码模式
在 Spring 5.1 之后,WebClient 默认使用 TEMPLATE_AND_VALUES 模式,这是最安全的做法。它会将变量视为透明数据。 例如,如果变量是 "foo+bar":
- 不编码:会被解析为
"foo bar"(空格)。 - 正确编码:会变为
"foo%2Bbar",确保接收端还原后仍是"foo+bar"。 在使用RestTemplate时,建议手动调用.encode()或配置DefaultUriBuilderFactory来统一编码策略。