Java中如何正确比较URI?解决参数、大小写等匹配问题
URI匹配优化方案:处理带参数、大小写、末尾斜杠的场景
你当前的直接字符串匹配方案无法应对URI的多种变体(查询参数、域名大小写、末尾斜杠等),核心解决思路是先对URI做标准化处理,再进行匹配,避免手动字符串操作的繁琐和bug。
一、核心思路
将存储的允许URI和输入的请求URI都转换为统一的标准格式:
- 协议(scheme)统一小写(如
HTTPS://→https://) - 域名(host)统一小写(如
MyUrl.com→myurl.com) - 路径(path)统一去除末尾斜杠(如
/foo/→/foo,根路径/保留) - 忽略查询参数(query)和片段(fragment)
二、代码实现(基于JDK原生API)
1. 编写URI标准化工具方法
用JDK自带的java.net.URI解析URI,避免手动正则处理:
private static String normalizeUri(String uriStr) throws URISyntaxException { URI uri = new URI(uriStr); // 处理协议:统一小写 String scheme = uri.getScheme().toLowerCase(); // 处理域名:统一小写 String host = uri.getHost().toLowerCase(); // 处理路径:去除末尾斜杠(根路径"/"除外) String path = uri.getPath(); if (path != null && path.length() > 1 && path.endsWith("/")) { path = path.substring(0, path.length() - 1); } // 重新构建标准化URI,忽略查询参数和片段 return new URI(scheme, host, path, null, null).toString(); }
2. 初始化标准化后的允许URI列表
在初始化ALLOWED_URIS时,先对每个配置的URI做标准化:
private static final Map<String, Set<String>> ALLOWED_URIS; static { try { ALLOWED_URIS = ImmutableMap.<String, Set<String>>builder() .put(normalizeUri("https://myurl.com/foo"), ImmutableSet.of("GET")) .put(normalizeUri("https://anotherurl/bar"), ImmutableSet.of("GET")) .put(normalizeUri("https://example.com/foo2"), ImmutableSet.of("GET")) .build(); } catch (URISyntaxException e) { throw new RuntimeException("初始化允许URI列表失败", e); } }
3. 修改匹配逻辑
对输入的URI先做标准化,再去Map中匹配:
private static boolean isAllowed(final String uri, final String action) { try { String normalizedInputUri = normalizeUri(uri); return ALLOWED_URIS.getOrDefault(normalizedInputUri, emptySet()).contains(action); } catch (URISyntaxException e) { // 非法URI直接判定为不允许 return false; } }
三、可选工具类推荐
如果是Spring项目,可以用org.springframework.web.util.UriComponentsBuilder简化标准化逻辑:
import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; private static String normalizeUri(String uriStr) { UriComponents components = UriComponentsBuilder.fromUriString(uriStr).build(); String scheme = components.getScheme().toLowerCase(); String host = components.getHost().toLowerCase(); String path = components.getPath(); if (path != null && path.length() > 1 && path.endsWith("/")) { path = path.substring(0, path.length() - 1); } return UriComponentsBuilder.newInstance() .scheme(scheme) .host(host) .path(path) .build() .toUriString(); }
如果使用Guava库,com.google.common.net.Urls类的normalize方法也能处理部分标准化场景,但需注意它的默认行为(比如保留末尾斜杠),可能需要额外调整。
四、注意事项
- 永远用URI解析库处理,不要手动写正则切割字符串,避免遗漏边缘场景(如带端口的URI、特殊字符路径等)
- 标准化规则要前后一致:允许列表和输入URI必须用同一套规则处理
- 对于非法URI(格式错误),直接返回不允许,避免抛出异常影响业务流程
内容的提问来源于stack exchange,提问作者Robert O'Neal
相关产品推荐
相关产品推荐

