12

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?

tan9
  • 3,490
  • 2
  • 18
  • 21
amitection
  • 2,696
  • 8
  • 25
  • 46
  • 1
    Does this answer your question? [Spring: Difference of /\*\* and /\* with regards to paths](https://stackoverflow.com/questions/12569308/spring-difference-of-and-with-regards-to-paths) – Tijmen May 04 '22 at 10:55
  • add **spring.mvc.pathmatch.matching-strategy: ANT_PATH_MATCHER** to your application.yaml file – Logicalj Jul 28 '22 at 14:51

3 Answers3

16

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"
informatik01
  • 16,038
  • 10
  • 74
  • 104
rhinds
  • 9,976
  • 13
  • 68
  • 111
15

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"))
    }
}
Ilya Serbis
  • 21,149
  • 6
  • 87
  • 74
3

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

}

enter image description here

Rahul
  • 18,271
  • 7
  • 41
  • 60