Skip to content

Commit 8441b55

Browse files
committed
Polish Usage Updates
1 parent 470966b commit 8441b55

File tree

6 files changed

+16
-349
lines changed

6 files changed

+16
-349
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ private static String computeErrorMessage(Collection<? extends ServletRegistrati
268268
This method cannot decide whether these patterns are Spring MVC patterns or not. \
269269
This is because there is more than one mappable servlet in your servlet context: %s.
270270
271-
To address this, please create one RequestMatchers#servlet for each servlet that has \
271+
To address this, please create one PathPatternRequestMatcher#servletPath for each servlet that has \
272272
authorized endpoints and use them to construct request matchers manually.
273273
""";
274274
Map<String, Collection<String>> mappings = new LinkedHashMap<>();

config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
6565
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
6666
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
67-
import org.springframework.security.web.util.matcher.RequestMatchers;
67+
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
6868
import org.springframework.test.web.servlet.MockMvc;
6969
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
7070
import org.springframework.test.web.servlet.request.RequestPostProcessor;
@@ -1343,11 +1343,11 @@ static class MvcRequestMatcherBuilderConfig {
13431343

13441344
@Bean
13451345
SecurityFilterChain security(HttpSecurity http) throws Exception {
1346-
RequestMatchers.Builder mvc = RequestMatchers.servletPath("/mvc");
1346+
PathPatternRequestMatcher.Builder mvc = PathPatternRequestMatcher.servletPath("/mvc");
13471347
// @formatter:off
13481348
http
13491349
.authorizeHttpRequests((authorize) -> authorize
1350-
.requestMatchers(mvc.pathPatterns("/path/**").matcher()).hasRole("USER")
1350+
.requestMatchers(mvc.pattern("/path/**").matcher()).hasRole("USER")
13511351
)
13521352
.httpBasic(withDefaults());
13531353
// @formatter:on

docs/modules/ROOT/pages/migration-7/web.adoc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,22 +119,22 @@ Instead of taking this responsibility away from developers, now it is simpler to
119119

120120
[method,java]
121121
----
122-
RequestMatchers.Builder servlet = RequestMatchers.servlet("/mvc");
122+
PathPatternRequestParser.Builder servlet = PathPatternRequestParser.servletPath("/mvc");
123123
http
124124
.authorizeHttpRequests((authorize) -> authorize
125-
.requestMatchers(servlet.uris("/orders/**").matcher()).authenticated()
125+
.requestMatchers(servlet.pattern("/orders/**").matcher()).authenticated()
126126
)
127127
----
128128

129129

130-
For paths that belong to the default servlet, use `RequestMatchers.request()` instead:
130+
For paths that belong to the default servlet, use `PathPatternRequestParser.path()` instead:
131131

132132
[method,java]
133133
----
134-
RequestMatchers.Builder request = RequestMatchers.request();
134+
PathPatternRequestParser.Builder request = PathPatternRequestParser.path();
135135
http
136136
.authorizeHttpRequests((authorize) -> authorize
137-
.requestMatchers(request.uris("/js/**").matcher()).authenticated()
137+
.requestMatchers(request.pattern("/js/**").matcher()).authenticated()
138138
)
139139
----
140140

docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -590,14 +590,14 @@ Java::
590590
+
591591
[source,java,role="primary"]
592592
----
593-
import static org.springframework.security.web.servlet.util.matcher.RequestMatchers.servlet;
593+
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.servletPath;
594594
595595
@Bean
596596
SecurityFilterChain appEndpoints(HttpSecurity http) {
597597
http
598598
.authorizeHttpRequests((authorize) -> authorize
599-
.requestMatchers(servlet("/spring-mvc").uris("/admin/**")).hasAuthority("admin")
600-
.requestMatchers(servlet("/spring-mvc").uris("/my/controller/**")).hasAuthority("controller")
599+
.requestMatchers(servletPath("/spring-mvc").pattern("/admin/**").matcher()).hasAuthority("admin")
600+
.requestMatchers(servletPath("/spring-mvc").pattern("/my/controller/**").matcher()).hasAuthority("controller")
601601
.anyRequest().authenticated()
602602
);
603603
@@ -639,15 +639,15 @@ With Java, note that the `ServletRequestMatcherBuilders` return value can be reu
639639

640640
[source,java,role="primary"]
641641
----
642-
import static org.springframework.security.web.servlet.util.matcher.RequestMatchers.servlet;
642+
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.servletPath;
643643
644644
@Bean
645645
SecurityFilterChain appEndpoints(HttpSecurity http) {
646-
RequestMatchers.Builder mvc = servlet("/spring-mvc");
646+
PathPatternRequestMatcher.Builder mvc = servletPath("/spring-mvc");
647647
http
648648
.authorizeHttpRequests((authorize) -> authorize
649-
.requestMatchers(mvc.uris("/admin/**")).hasAuthority("admin")
650-
.requestMatchers(mvc.uris("/my/controller/**")).hasAuthority("controller")
649+
.requestMatchers(mvc.pattern("/admin/**").matcher()).hasAuthority("admin")
650+
.requestMatchers(mvc.pattern("/my/controller/**").matcher()).hasAuthority("controller")
651651
.anyRequest().authenticated()
652652
);
653653

web/src/main/java/org/springframework/security/web/util/matcher/RequestMatchers.java

Lines changed: 0 additions & 279 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,7 @@
1616

1717
package org.springframework.security.web.util.matcher;
1818

19-
import java.nio.charset.StandardCharsets;
20-
import java.util.Collection;
21-
import java.util.LinkedHashMap;
2219
import java.util.List;
23-
import java.util.Map;
24-
import java.util.Objects;
25-
import java.util.concurrent.atomic.AtomicReference;
26-
27-
import jakarta.servlet.DispatcherType;
28-
import jakarta.servlet.RequestDispatcher;
29-
import jakarta.servlet.ServletContext;
30-
import jakarta.servlet.ServletRegistration;
31-
import jakarta.servlet.http.HttpServletMapping;
32-
import jakarta.servlet.http.HttpServletRequest;
33-
import jakarta.servlet.http.MappingMatch;
34-
35-
import org.springframework.http.HttpMethod;
36-
import org.springframework.lang.Nullable;
37-
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
38-
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
39-
import org.springframework.util.Assert;
40-
import org.springframework.util.ObjectUtils;
41-
import org.springframework.web.util.UriUtils;
42-
import org.springframework.web.util.WebUtils;
43-
import org.springframework.web.util.pattern.PathPattern;
44-
import org.springframework.web.util.pattern.PathPatternParser;
4520

4621
/**
4722
* A factory class to create {@link RequestMatcher} instances.
@@ -85,261 +60,7 @@ public static RequestMatcher not(RequestMatcher matcher) {
8560
return (request) -> !matcher.matches(request);
8661
}
8762

88-
/**
89-
* Create {@link RequestMatcher}s whose URIs do not have a servlet path prefix
90-
* <p>
91-
* When there is no context path, then these URIs are effectively absolute.
92-
* @return a {@link Builder} that treats URIs as relative to the context path, if any
93-
* @since 6.5
94-
*/
95-
public static Builder request() {
96-
return new Builder();
97-
}
98-
99-
/**
100-
* Create {@link RequestMatcher}s whose URIs are relative to the given
101-
* {@code servletPath} prefix.
102-
*
103-
* <p>
104-
* The {@code servletPath} must correlate to a value that would match the result of
105-
* {@link HttpServletRequest#getServletPath()} and its corresponding servlet.
106-
*
107-
* <p>
108-
* That is, if you have a servlet mapping of {@code /path/*}, then
109-
* {@link HttpServletRequest#getServletPath()} would return {@code /path} and so
110-
* {@code /path} is what is specified here.
111-
*
112-
* Specify the path here without the trailing {@code /*}.
113-
* @return a {@link Builder} that treats URIs as relative to the given
114-
* {@code servletPath}
115-
* @since 6.5
116-
*/
117-
public static Builder servletPath(String servletPath) {
118-
Assert.notNull(servletPath, "servletPath cannot be null");
119-
Assert.isTrue(servletPath.startsWith("/"), "servletPath must start with '/'");
120-
Assert.isTrue(!servletPath.endsWith("/"), "servletPath must not end with a slash");
121-
Assert.isTrue(!servletPath.contains("*"), "servletPath must not contain a star");
122-
return new Builder(servletPath);
123-
}
124-
12563
private RequestMatchers() {
12664
}
12765

128-
/**
129-
* A builder for specifying various elements of a request for the purpose of creating
130-
* a {@link RequestMatcher}.
131-
*
132-
* <p>
133-
* For example, if Spring MVC is deployed to `/mvc` and another servlet to `/other`,
134-
* then you can do:
135-
* </p>
136-
*
137-
* <code>
138-
* http
139-
* .authorizeHttpRequests((authorize) -> authorize
140-
* .requestMatchers(servlet("/mvc").uris("/user/**")).hasAuthority("user")
141-
* .requestMatchers(servlet("/other").uris("/admin/**")).hasAuthority("admin")
142-
* )
143-
* ...
144-
* </code>
145-
*
146-
* @author Josh Cummings
147-
* @since 6.5
148-
*/
149-
public static final class Builder {
150-
151-
private final RequestMatcher servletPath;
152-
153-
private final RequestMatcher methods;
154-
155-
private final RequestMatcher uris;
156-
157-
private final RequestMatcher dispatcherTypes;
158-
159-
private Builder() {
160-
this(AnyRequestMatcher.INSTANCE, AnyRequestMatcher.INSTANCE, AnyRequestMatcher.INSTANCE,
161-
AnyRequestMatcher.INSTANCE);
162-
}
163-
164-
private Builder(String servletPath) {
165-
this(new ServletPathRequestMatcher(servletPath), AnyRequestMatcher.INSTANCE, AnyRequestMatcher.INSTANCE,
166-
AnyRequestMatcher.INSTANCE);
167-
}
168-
169-
private Builder(RequestMatcher servletPath, RequestMatcher methods, RequestMatcher uris,
170-
RequestMatcher dispatcherTypes) {
171-
this.servletPath = servletPath;
172-
this.methods = methods;
173-
this.uris = uris;
174-
this.dispatcherTypes = dispatcherTypes;
175-
}
176-
177-
/**
178-
* Match requests with any of these methods
179-
* @param methods the {@link HttpMethod} to match
180-
* @return the {@link Builder} for more configuration
181-
*/
182-
public Builder methods(HttpMethod... methods) {
183-
RequestMatcher[] matchers = new RequestMatcher[methods.length];
184-
for (int i = 0; i < methods.length; i++) {
185-
matchers[i] = new HttpMethodRequestMatcher(methods[i]);
186-
}
187-
return new Builder(this.servletPath, anyOf(matchers), this.uris, this.dispatcherTypes);
188-
}
189-
190-
/**
191-
* Match requests with any of these path patterns
192-
*
193-
* <p>
194-
* Path patterns always start with a slash and may contain placeholders. They can
195-
* also be followed by {@code /**} to signify all URIs under a given path.
196-
*
197-
* <p>
198-
* These must be specified relative to any servlet path prefix (meaning you should
199-
* exclude the context path and any servlet path prefix in stating your pattern).
200-
*
201-
* <p>
202-
* The following are valid patterns and their meaning
203-
* <ul>
204-
* <li>{@code /path} - match exactly and only `/path`</li>
205-
* <li>{@code /path/**} - match `/path` and any of its descendents</li>
206-
* <li>{@code /path/{value}/**} - match `/path/subdirectory` and any of its
207-
* descendents, capturing the value of the subdirectory in
208-
* {@link RequestAuthorizationContext#getVariables()}</li>
209-
* </ul>
210-
*
211-
* <p>
212-
* A more comprehensive list can be found at {@link PathPattern}.
213-
* @param pathPatterns the path patterns to match
214-
* @return the {@link Builder} for more configuration
215-
*/
216-
public Builder pathPatterns(String... pathPatterns) {
217-
RequestMatcher[] matchers = new RequestMatcher[pathPatterns.length];
218-
for (int i = 0; i < pathPatterns.length; i++) {
219-
Assert.isTrue(pathPatterns[i].startsWith("/"), "path patterns must start with /");
220-
PathPatternParser parser = PathPatternParser.defaultInstance;
221-
matchers[i] = new PathPatternRequestMatcher(parser.parse(pathPatterns[i]));
222-
}
223-
return new Builder(this.servletPath, this.methods, anyOf(matchers), this.dispatcherTypes);
224-
}
225-
226-
/**
227-
* Match requests with any of these {@link PathPattern}s
228-
*
229-
* <p>
230-
* Use this when you have a non-default {@link PathPatternParser}
231-
* @param pathPatterns the URIs to match
232-
* @return the {@link Builder} for more configuration
233-
*/
234-
public Builder pathPatterns(PathPattern... pathPatterns) {
235-
RequestMatcher[] matchers = new RequestMatcher[pathPatterns.length];
236-
for (int i = 0; i < pathPatterns.length; i++) {
237-
matchers[i] = new PathPatternRequestMatcher(pathPatterns[i]);
238-
}
239-
return new Builder(this.servletPath, this.methods, anyOf(matchers), this.dispatcherTypes);
240-
}
241-
242-
/**
243-
* Match requests with any of these dispatcherTypes
244-
* @param dispatcherTypes the {@link DispatcherType}s to match
245-
* @return the {@link Builder} for more configuration
246-
*/
247-
public Builder dispatcherTypes(DispatcherType... dispatcherTypes) {
248-
RequestMatcher[] matchers = new RequestMatcher[dispatcherTypes.length];
249-
for (int i = 0; i < dispatcherTypes.length; i++) {
250-
matchers[i] = new DispatcherTypeRequestMatcher(dispatcherTypes[i]);
251-
}
252-
return new Builder(this.servletPath, this.methods, this.uris, anyOf(matchers));
253-
}
254-
255-
/**
256-
* Create the {@link RequestMatcher}
257-
* @return the composite {@link RequestMatcher}
258-
*/
259-
public RequestMatcher matcher() {
260-
return allOf(this.servletPath, this.methods, this.uris, this.dispatcherTypes);
261-
}
262-
263-
}
264-
265-
private record HttpMethodRequestMatcher(HttpMethod method) implements RequestMatcher {
266-
267-
@Override
268-
public boolean matches(HttpServletRequest request) {
269-
return this.method.name().equals(request.getMethod());
270-
}
271-
272-
@Override
273-
public String toString() {
274-
return "HttpMethod [" + this.method + "]";
275-
}
276-
277-
}
278-
279-
private static final class ServletPathRequestMatcher implements RequestMatcher {
280-
281-
private final String path;
282-
283-
private final AtomicReference<Boolean> servletExists = new AtomicReference();
284-
285-
ServletPathRequestMatcher(String servletPath) {
286-
this.path = servletPath;
287-
}
288-
289-
@Override
290-
public boolean matches(HttpServletRequest request) {
291-
Assert.isTrue(servletExists(request), () -> this.path + "/* does not exist in your servlet registration "
292-
+ registrationMappings(request));
293-
return Objects.equals(this.path, getServletPathPrefix(request));
294-
}
295-
296-
private boolean servletExists(HttpServletRequest request) {
297-
return this.servletExists.updateAndGet((value) -> {
298-
if (value != null) {
299-
return value;
300-
}
301-
if (request.getAttribute("org.springframework.test.web.servlet.MockMvc.MVC_RESULT_ATTRIBUTE") != null) {
302-
return true;
303-
}
304-
for (ServletRegistration registration : request.getServletContext()
305-
.getServletRegistrations()
306-
.values()) {
307-
if (registration.getMappings().contains(this.path + "/*")) {
308-
return true;
309-
}
310-
}
311-
return false;
312-
});
313-
}
314-
315-
private Map<String, Collection<String>> registrationMappings(HttpServletRequest request) {
316-
Map<String, Collection<String>> map = new LinkedHashMap<>();
317-
ServletContext servletContext = request.getServletContext();
318-
for (ServletRegistration registration : servletContext.getServletRegistrations().values()) {
319-
map.put(registration.getName(), registration.getMappings());
320-
}
321-
return map;
322-
}
323-
324-
@Nullable
325-
private static String getServletPathPrefix(HttpServletRequest request) {
326-
HttpServletMapping mapping = (HttpServletMapping) request.getAttribute(RequestDispatcher.INCLUDE_MAPPING);
327-
mapping = (mapping != null) ? mapping : request.getHttpServletMapping();
328-
if (ObjectUtils.nullSafeEquals(mapping.getMappingMatch(), MappingMatch.PATH)) {
329-
String servletPath = (String) request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE);
330-
servletPath = (servletPath != null) ? servletPath : request.getServletPath();
331-
servletPath = servletPath.endsWith("/") ? servletPath.substring(0, servletPath.length() - 1)
332-
: servletPath;
333-
return UriUtils.encodePath(servletPath, StandardCharsets.UTF_8);
334-
}
335-
return null;
336-
}
337-
338-
@Override
339-
public String toString() {
340-
return "ServletPath [" + this.path + "]";
341-
}
342-
343-
}
344-
34566
}

0 commit comments

Comments
 (0)