diff --git a/web/src/main/java/org/springframework/security/web/util/AntPathRequestMatcher.java b/web/src/main/java/org/springframework/security/web/util/AntPathRequestMatcher.java index 1fdcde2b5d..482e7bcec1 100644 --- a/web/src/main/java/org/springframework/security/web/util/AntPathRequestMatcher.java +++ b/web/src/main/java/org/springframework/security/web/util/AntPathRequestMatcher.java @@ -24,15 +24,18 @@ import org.springframework.util.StringUtils; /** * Matcher which compares a pre-defined ant-style pattern against the URL * ({@code servletPath + pathInfo}) of an {@code HttpServletRequest}. - * The query string of the URL is ignored and matching is case-insensitive. + * The query string of the URL is ignored and matching is case-insensitive or case-sensitive depending on + * the arguments passed into the constructor. *

* Using a pattern value of {@code /**} or {@code **} is treated as a universal * match, which will match any request. Patterns which end with {@code /**} (and have no other wildcards) * are optimized by using a substring match — a pattern of {@code /aaa/**} will match {@code /aaa}, * {@code /aaa/} and any sub-directories, such as {@code /aaa/bbb/ccc}. + *

*

* For all other cases, Spring's {@link AntPathMatcher} is used to perform the match. See the Spring documentation * for this class for comprehensive information on the syntax used. + *

* * @author Luke Taylor * @author Rob Winch @@ -47,31 +50,56 @@ public final class AntPathRequestMatcher implements RequestMatcher { private final Matcher matcher; private final String pattern; private final HttpMethod httpMethod; + private final boolean caseSensitive; /** - * Creates a matcher with the specific pattern which will match all HTTP methods. + * Creates a matcher with the specific pattern which will match all HTTP + * methods in a case insensitive manner. * - * @param pattern the ant pattern to use for matching + * @param pattern + * the ant pattern to use for matching */ public AntPathRequestMatcher(String pattern) { this(pattern, null); } /** - * Creates a matcher with the supplied pattern which will match all HTTP methods. + * Creates a matcher with the supplied pattern and HTTP method in a case + * insensitive manner. * - * @param pattern the ant pattern to use for matching - * @param httpMethod the HTTP method. The {@code matches} method will return false if the incoming request doesn't - * have the same method. + * @param pattern + * the ant pattern to use for matching + * @param httpMethod + * the HTTP method. The {@code matches} method will return false + * if the incoming request doesn't have the same method. */ public AntPathRequestMatcher(String pattern, String httpMethod) { + this(pattern,httpMethod,false); + } + + /** + * Creates a matcher with the supplied pattern which will match the + * specified Http method + * + * @param pattern + * the ant pattern to use for matching + * @param httpMethod + * the HTTP method. The {@code matches} method will return false + * if the incoming request doesn't doesn't have the same method. + * @param caseSensitive + * true if the matcher should consider case, else false + */ + public AntPathRequestMatcher(String pattern, String httpMethod, boolean caseSensitive) { Assert.hasText(pattern, "Pattern cannot be null or empty"); + this.caseSensitive = caseSensitive; if (pattern.equals(MATCH_ALL) || pattern.equals("**")) { pattern = MATCH_ALL; matcher = null; } else { - pattern = pattern.toLowerCase(); + if(!caseSensitive) { + pattern = pattern.toLowerCase(); + } // If the pattern ends with {@code /**} and has no other wildcards, then optimize to a sub-path match if (pattern.endsWith(MATCH_ALL) && pattern.indexOf('?') == -1 && @@ -126,7 +154,9 @@ public final class AntPathRequestMatcher implements RequestMatcher { url += request.getPathInfo(); } - url = url.toLowerCase(); + if(!caseSensitive) { + url = url.toLowerCase(); + } return url; } @@ -142,7 +172,8 @@ public final class AntPathRequestMatcher implements RequestMatcher { } AntPathRequestMatcher other = (AntPathRequestMatcher)obj; return this.pattern.equals(other.pattern) && - this.httpMethod == other.httpMethod; + this.httpMethod == other.httpMethod && + this.caseSensitive == other.caseSensitive; } @Override diff --git a/web/src/test/java/org/springframework/security/web/util/AntPathRequestMatcherTests.java b/web/src/test/java/org/springframework/security/web/util/AntPathRequestMatcherTests.java index 4adcc7c5b6..52205b524a 100644 --- a/web/src/test/java/org/springframework/security/web/util/AntPathRequestMatcherTests.java +++ b/web/src/test/java/org/springframework/security/web/util/AntPathRequestMatcherTests.java @@ -12,6 +12,7 @@ */ package org.springframework.security.web.util; +import static org.fest.assertions.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -112,6 +113,17 @@ public class AntPathRequestMatcherTests { assertFalse(matcher.matches(request)); } + @Test + public void caseSensitive() throws Exception { + MockHttpServletRequest request = createRequest("/UPPER"); + assertThat(new AntPathRequestMatcher("/upper",null,true).matches(request)).isFalse(); + assertThat(new AntPathRequestMatcher("/upper","POST",true).matches(request)).isFalse(); + assertThat(new AntPathRequestMatcher("/upper","GET",true).matches(request)).isFalse(); + + assertThat(new AntPathRequestMatcher("/upper",null,false).matches(request)).isTrue(); + assertThat(new AntPathRequestMatcher("/upper","POST",false).matches(request)).isTrue(); + } + @Test public void equalsBehavesCorrectly() throws Exception { // Both universal wildcard options should be equal @@ -121,6 +133,7 @@ public class AntPathRequestMatcherTests { assertFalse(new AntPathRequestMatcher("/xyz", "POST").equals(new AntPathRequestMatcher("/xyz", "GET"))); assertFalse(new AntPathRequestMatcher("/xyz").equals(new AntPathRequestMatcher("/xxx"))); assertFalse(new AntPathRequestMatcher("/xyz").equals(new AnyRequestMatcher())); + assertFalse(new AntPathRequestMatcher("/xyz","GET", false).equals(new AntPathRequestMatcher("/xyz","GET", true))); } @Test