I have several different controllers, configured with endpoints like below. Where {id} is a numeric @PathVariable.
@RequestMapping(value = "/noun1/noun2/{id}/verb1")
@RequestMapping(value = "/noun1/noun2/{id}/verb2")
@RequestMapping(value = "/noun1/noun2/{id}/verb3/verb4")
@RequestMapping(value = "/noun1/noun2/noun3/verb5")
Using HttpSecurity, I want to implement security around all endpoints that have {id} in them. So I created an ant pattern like this:
"/noun1/noun2/{id}/**"
The ant pattern is correctly matching on the endpoints with {id} in them. However, the ant pattern is also matching on the last endpoint, setting id = noun3. Is there a way with ant patterns to only match the endpoints containing {id}?
Advertisement
Answer
How your ant pattern should be depends on what {id} can be. The {id} in your ant matcher is not directly related to your {id} in your RequestMapping annotation.
Spring Security just checks, with the ant matcher, whether the path matches the provided pattern. In your case {id} matches any string and stores the matched value in a URI template variable named id for later use.
To specify the what is considered an {id} you can provide an regex. If your id consists of digits your matcher could be "/noun1/noun2/{regex:\d+}/**":
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.springframework.util.AntPathMatcher;
class AntMatcherTest {
@Test
void antMatcher() {
AntPathMatcher antPathMatcher = new AntPathMatcher();
String pattern = "/noun1/noun2/{id:\d+}/**";
assertFalse(antPathMatcher.match(pattern, "/noun1/noun2/noun3/verb5"));
assertTrue(antPathMatcher.match(pattern, "/noun1/noun2/1/verb1"));
assertTrue(antPathMatcher.match(pattern, "/noun1/noun2/2/verb2"));
assertTrue(antPathMatcher.match(pattern, "/noun1/noun2/3/verb3/verb4"));
}
}