提交 6f2c9689 编写于 作者: R Rossen Stoyanchev

Support strict URI variable encoding

The DefaulUriTemplateHandler now provides a strictEncoding property
which if turned on encodes everything outside the reserved char set.

This is in contrast to the default policy of encoding only illegal
charaters depending on the URI component type.

Issue: SPR-11652
上级 e98738d8
......@@ -16,8 +16,10 @@
package org.springframework.web.util;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -36,6 +38,8 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler {
private boolean parsePath;
private boolean strictEncoding;
/**
* Configure a base URL to prepend URI templates with. The base URL must
......@@ -83,19 +87,45 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler {
return this.parsePath;
}
/**
* Whether to encode characters outside the unreserved set as defined in
* <a href="https://tools.ietf.org/html/rfc3986#section-2">RFC 3986 Section 2</a>.
* This ensures a URI variable value will not contain any characters with a
* reserved purpose.
* <p>By default this is set to {@code false} in which case only characters
* illegal for the given URI component are encoded. For example when expanding
* a URI variable into a path segment the "/" character is illegal and
* encoded. The ";" character however is legal and not encoded even though
* it has a reserved purpose.
* <p><strong>Note:</strong> this property supersedes the need to also set
* the {@link #setParsePath parsePath} property.
* @param strictEncoding whether to perform strict encoding
* @since 4.3
*/
public void setStrictEncoding(boolean strictEncoding) {
this.strictEncoding = strictEncoding;
}
/**
* Whether to strictly encode any character outside the unreserved set.
*/
public boolean isStrictEncoding() {
return this.strictEncoding;
}
@Override
public URI expand(String uriTemplate, Map<String, ?> uriVariables) {
UriComponentsBuilder builder = initUriComponentsBuilder(uriTemplate);
UriComponents url = builder.build().expand(uriVariables).encode();
return insertBaseUrl(url);
UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate);
UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables);
return insertBaseUrl(uriComponents);
}
@Override
public URI expand(String uriTemplate, Object... uriVariables) {
UriComponentsBuilder builder = initUriComponentsBuilder(uriTemplate);
UriComponents url = builder.build().expand(uriVariables).encode();
return insertBaseUrl(url);
UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate);
UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables);
return insertBaseUrl(uriComponents);
}
/**
......@@ -105,7 +135,7 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler {
*/
protected UriComponentsBuilder initUriComponentsBuilder(String uriTemplate) {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uriTemplate);
if (shouldParsePath()) {
if (shouldParsePath() && !isStrictEncoding()) {
List<String> pathSegments = builder.build().getPathSegments();
builder.replacePath(null);
for (String pathSegment : pathSegments) {
......@@ -115,6 +145,43 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler {
return builder;
}
protected UriComponents expandAndEncode(UriComponentsBuilder builder, Map<String, ?> uriVariables) {
if (!isStrictEncoding()) {
return builder.build().expand(uriVariables).encode();
}
else {
Map<String, Object> encodedUriVars = new HashMap<String, Object>(uriVariables.size());
for (Map.Entry<String, ?> entry : uriVariables.entrySet()) {
encodedUriVars.put(entry.getKey(), encodeValue(entry.getValue()));
}
return builder.build().expand(encodedUriVars);
}
}
protected UriComponents expandAndEncode(UriComponentsBuilder builder, Object[] uriVariables) {
if (!isStrictEncoding()) {
return builder.build().expand(uriVariables).encode();
}
else {
Object[] encodedUriVars = new Object[uriVariables.length];
for (int i = 0; i < uriVariables.length; i++) {
encodedUriVars[i] = encodeValue(uriVariables[i]);
}
return builder.build().expand(encodedUriVars);
}
}
private String encodeValue(Object value) {
String stringValue = (value != null ? value.toString() : "");
try {
return UriUtils.encode(stringValue, "UTF-8");
}
catch (UnsupportedEncodingException ex) {
// Should never happen
throw new IllegalStateException("Failed to encode URI variable", ex);
}
}
/**
* Invoked after the URI template has been expanded and encoded to prepend
* the configured {@link #setBaseUrl(String) baseUrl} if any.
......@@ -122,10 +189,10 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler {
* @return the final URI
*/
protected URI insertBaseUrl(UriComponents uriComponents) {
if (getBaseUrl() == null || uriComponents.getHost() != null) {
return uriComponents.toUri();
String url = uriComponents.toUriString();
if (getBaseUrl() != null && uriComponents.getHost() == null) {
url = getBaseUrl() + url;
}
String url = getBaseUrl() + uriComponents.toUriString();
try {
return new URI(url);
}
......
......@@ -49,7 +49,8 @@ public class DefaultUriTemplateHandlerTests {
}
@Test
public void expandWithFullPath() throws Exception {
public void parsePathOff() throws Exception {
this.handler.setParsePath(false);
Map<String, String> vars = new HashMap<>(2);
vars.put("hotel", "1");
vars.put("publicpath", "pics/logo.png");
......@@ -60,7 +61,7 @@ public class DefaultUriTemplateHandlerTests {
}
@Test
public void expandWithFullPathAndParsePathEnabled() throws Exception {
public void parsePathOn() throws Exception {
this.handler.setParsePath(true);
Map<String, String> vars = new HashMap<>(2);
vars.put("hotel", "1");
......@@ -72,4 +73,35 @@ public class DefaultUriTemplateHandlerTests {
assertEquals(expected, actual);
}
@Test
public void strictEncodingOff() throws Exception {
this.handler.setStrictEncoding(false);
Map<String, String> vars = new HashMap<>(2);
vars.put("userId", "john;doe");
String template = "http://www.example.com/user/{userId}/dashboard";
URI actual = this.handler.expand(template, vars);
URI expected = new URI("http://www.example.com/user/john;doe/dashboard");
assertEquals(expected, actual);
}
@Test
public void strictEncodingOnWithMap() throws Exception {
this.handler.setStrictEncoding(true);
Map<String, String> vars = new HashMap<>(2);
vars.put("userId", "john;doe");
String template = "http://www.example.com/user/{userId}/dashboard";
URI actual = this.handler.expand(template, vars);
URI expected = new URI("http://www.example.com/user/john%3Bdoe/dashboard");
assertEquals(expected, actual);
}
@Test
public void strictEncodingOnWithArray() throws Exception {
this.handler.setStrictEncoding(true);
String template = "http://www.example.com/user/{userId}/dashboard";
URI actual = this.handler.expand(template, "john;doe");
URI expected = new URI("http://www.example.com/user/john%3Bdoe/dashboard");
assertEquals(expected, actual);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册