I was trying to register certain URLs for a Filter when I noticed that there is a difference between /*
and /**
patterns.
@Bean
public FilterRegistrationBean tokenAuthenticationFilterBean() {
FilterRegistrationBean registration = new FilterRegistrationBean(tokenAuthenticationFilter);
registration.addUrlPatterns("/api/**","/ping","/api/*");
return registration;
}
What is the difference between these patterns?
Spring normally uses ant-style path matching patterns for URLs - if you look at the java docs for the AntPathMatcher you will see the explanation
The mapping matches URLs using the following rules: ? matches one character
* matches zero or more characters
** matches zero or more directories in a path
{spring:[a-z]+} matches the regexp [a-z]+ as a path variable named "spring"
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html
IMHO code worths 100 words in this case:
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.springframework.util.AntPathMatcher
class AntPathMatcherTests {
@Test
fun twoAsterisks() {
val pattern = "/api/balance/**"
val matcher = AntPathMatcher()
val matching = { path: String -> matcher.match(pattern, path) }
assertTrue(matching("/api/balance"))
assertTrue(matching("/api/balance/"))
assertTrue(matching("/api/balance/abc"))
assertTrue(matching("/api/balance/abc/"))
assertTrue(matching("/api/balance/abc/update"))
assertFalse(matching("/api/bala"))
}
@Test
fun oneAsterisk() {
val pattern = "/api/balance/*"
val matcher = AntPathMatcher()
val matching = { path: String -> matcher.match(pattern, path) }
assertTrue(matching("/api/balance/"))
assertTrue(matching("/api/balance/abc"))
assertFalse(matching("/api/bala"))
assertFalse(matching("/api/balance"))
assertFalse(matching("/api/balance/abc/"))
assertFalse(matching("/api/balance/abc/update"))
}
}
there is no /** in registration.addUrlPatterns as we can see in
Source code
private static boolean matchFiltersURL(String testPath, String requestPath) {
if (testPath == null)
return false;
// Case 1 - Exact Match
if (testPath.equals(requestPath))
return true;
// Case 2 - Path Match ("/.../*")
if (testPath.equals("/*"))
return true;
if (testPath.endsWith("/*")) {
if (testPath.regionMatches(0, requestPath, 0,
testPath.length() - 2)) {
if (requestPath.length() == (testPath.length() - 2)) {
return true;
} else if ('/' == requestPath.charAt(testPath.length() - 2)) {
return true;
}
}
return false;
}
// Case 3 - Extension Match
if (testPath.startsWith("*.")) {
int slash = requestPath.lastIndexOf('/');
int period = requestPath.lastIndexOf('.');
if ((slash >= 0) && (period > slash)
&& (period != requestPath.length() - 1)
&& ((requestPath.length() - period)
== (testPath.length() - 1))) {
return testPath.regionMatches(2, requestPath, period + 1,
testPath.length() - 2);
}
}
// Case 4 - "Default" Match
return false; // NOTE - Not relevant for selecting filters
}