Skip to content

Commit e4c13e3

Browse files
author
Rob Winch
committed
Add MvcRequestMatcher
Fixes gh-3964
1 parent 13bc70f commit e4c13e3

File tree

26 files changed

+918
-54
lines changed

26 files changed

+918
-54
lines changed

config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import org.apache.commons.logging.Log;
2727
import org.apache.commons.logging.LogFactory;
28+
2829
import org.springframework.security.config.annotation.web.builders.WebSecurity;
2930
import org.springframework.util.Assert;
3031
import org.springframework.web.filter.DelegatingFilterProxy;
@@ -57,7 +58,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
5758
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();
5859
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<SecurityConfigurer<O, B>>();
5960

60-
private final Map<Class<Object>, Object> sharedObjects = new HashMap<Class<Object>, Object>();
61+
private final Map<Class<? extends Object>, Object> sharedObjects = new HashMap<Class<? extends Object>, Object>();
6162

6263
private final boolean allowConfigurersOfSameType;
6364

@@ -155,7 +156,7 @@ public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Excepti
155156
*/
156157
@SuppressWarnings("unchecked")
157158
public <C> void setSharedObject(Class<C> sharedType, C object) {
158-
this.sharedObjects.put((Class<Object>) sharedType, object);
159+
this.sharedObjects.put(sharedType, object);
159160
}
160161

161162
/**
@@ -173,7 +174,7 @@ public <C> C getSharedObject(Class<C> sharedType) {
173174
* Gets the shared objects
174175
* @return
175176
*/
176-
public Map<Class<Object>, Object> getSharedObjects() {
177+
public Map<Class<? extends Object>, Object> getSharedObjects() {
177178
return Collections.unmodifiableMap(this.sharedObjects);
178179
}
179180

@@ -300,7 +301,7 @@ public O objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
300301
* @return the possibly modified Object to use
301302
*/
302303
protected <P> P postProcess(P object) {
303-
return (P) this.objectPostProcessor.postProcess(object);
304+
return this.objectPostProcessor.postProcess(object);
304305
}
305306

306307
/**

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@
1919
import java.util.Arrays;
2020
import java.util.List;
2121

22+
import org.springframework.context.ApplicationContext;
2223
import org.springframework.http.HttpMethod;
2324
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry;
25+
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
2426
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
2527
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
2628
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
2729
import org.springframework.security.web.util.matcher.RequestMatcher;
30+
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
2831

2932
/**
3033
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for
@@ -39,6 +42,12 @@
3942
public abstract class AbstractRequestMatcherRegistry<C> {
4043
private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
4144

45+
private ApplicationContext context;
46+
47+
protected final void setApplicationContext(ApplicationContext context) {
48+
this.context = context;
49+
}
50+
4251
/**
4352
* Maps any request.
4453
*
@@ -92,6 +101,57 @@ public C antMatchers(String... antPatterns) {
92101
return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));
93102
}
94103

104+
/**
105+
* <p>
106+
* Maps an {@link MvcRequestMatcher} that does not care which {@link HttpMethod} is
107+
* used. This matcher will use the same rules that Spring MVC uses for matching. For
108+
* example, often times a mapping of the path "/path" will match on "/path", "/path/",
109+
* "/path.html", etc.
110+
* </p>
111+
* <p>
112+
* If the current request will not be processed by Spring MVC, a reasonable default
113+
* using the pattern as a ant pattern will be used.
114+
* </p>
115+
*
116+
* @param mvcPatterns the patterns to match on. The rules for matching are defined by
117+
* Spring MVC
118+
* @return the object that is chained after creating the {@link RequestMatcher}.
119+
*/
120+
public C mvcMatchers(String... mvcPatterns) {
121+
return mvcMatchers(null, mvcPatterns);
122+
}
123+
124+
/**
125+
* <p>
126+
* Maps an {@link MvcRequestMatcher} that also specifies a specific {@link HttpMethod}
127+
* to match on. This matcher will use the same rules that Spring MVC uses for
128+
* matching. For example, often times a mapping of the path "/path" will match on
129+
* "/path", "/path/", "/path.html", etc.
130+
* </p>
131+
* <p>
132+
* If the current request will not be processed by Spring MVC, a reasonable default
133+
* using the pattern as a ant pattern will be used.
134+
* </p>
135+
*
136+
* @param method the HTTP method to match on
137+
* @param mvcPatterns the patterns to match on. The rules for matching are defined by
138+
* Spring MVC
139+
* @return the object that is chained after creating the {@link RequestMatcher}.
140+
*/
141+
public C mvcMatchers(HttpMethod method, String... mvcPatterns) {
142+
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector(
143+
this.context);
144+
List<RequestMatcher> matchers = new ArrayList<RequestMatcher>(mvcPatterns.length);
145+
for (String mvcPattern : mvcPatterns) {
146+
MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, mvcPattern);
147+
if (method != null) {
148+
matcher.setMethod(method);
149+
}
150+
matchers.add(matcher);
151+
}
152+
return chainRequestMatchers(matchers);
153+
}
154+
95155
/**
96156
* Maps a {@link List} of
97157
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher}

config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import javax.servlet.Filter;
2424
import javax.servlet.http.HttpServletRequest;
2525

26+
import org.springframework.context.ApplicationContext;
2627
import org.springframework.security.authentication.AuthenticationManager;
2728
import org.springframework.security.authentication.AuthenticationProvider;
2829
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
@@ -62,6 +63,7 @@
6263
import org.springframework.security.web.DefaultSecurityFilterChain;
6364
import org.springframework.security.web.PortMapper;
6465
import org.springframework.security.web.PortMapperImpl;
66+
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
6567
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
6668
import org.springframework.security.web.session.HttpSessionEventPublisher;
6769
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@@ -113,7 +115,7 @@ public final class HttpSecurity extends
113115
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
114116
implements SecurityBuilder<DefaultSecurityFilterChain>,
115117
HttpSecurityBuilder<HttpSecurity> {
116-
private final RequestMatcherConfigurer requestMatcherConfigurer = new RequestMatcherConfigurer();
118+
private final RequestMatcherConfigurer requestMatcherConfigurer;
117119
private List<Filter> filters = new ArrayList<Filter>();
118120
private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
119121
private FilterComparator comparator = new FilterComparator();
@@ -126,15 +128,24 @@ public final class HttpSecurity extends
126128
* @param sharedObjects the shared Objects to initialize the {@link HttpSecurity} with
127129
* @see WebSecurityConfiguration
128130
*/
131+
@SuppressWarnings("unchecked")
129132
public HttpSecurity(ObjectPostProcessor<Object> objectPostProcessor,
130133
AuthenticationManagerBuilder authenticationBuilder,
131-
Map<Class<Object>, Object> sharedObjects) {
134+
Map<Class<? extends Object>, Object> sharedObjects) {
132135
super(objectPostProcessor);
133136
Assert.notNull(authenticationBuilder, "authenticationBuilder cannot be null");
134137
setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);
135-
for (Map.Entry<Class<Object>, Object> entry : sharedObjects.entrySet()) {
136-
setSharedObject(entry.getKey(), entry.getValue());
138+
for (Map.Entry<Class<? extends Object>, Object> entry : sharedObjects
139+
.entrySet()) {
140+
setSharedObject((Class<Object>) entry.getKey(), entry.getValue());
137141
}
142+
ApplicationContext context = (ApplicationContext) sharedObjects
143+
.get(ApplicationContext.class);
144+
this.requestMatcherConfigurer = new RequestMatcherConfigurer(context);
145+
}
146+
147+
private ApplicationContext getContext() {
148+
return getSharedObject(ApplicationContext.class);
138149
}
139150

140151
/**
@@ -634,7 +645,8 @@ public RememberMeConfigurer<HttpSecurity> rememberMe() throws Exception {
634645
*/
635646
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
636647
throws Exception {
637-
return getOrApply(new ExpressionUrlAuthorizationConfigurer<HttpSecurity>())
648+
ApplicationContext context = getContext();
649+
return getOrApply(new ExpressionUrlAuthorizationConfigurer<HttpSecurity>(context))
638650
.getRegistry();
639651
}
640652

@@ -710,7 +722,8 @@ public ServletApiConfigurer<HttpSecurity> servletApi() throws Exception {
710722
* @throws Exception
711723
*/
712724
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
713-
return getOrApply(new CsrfConfigurer<HttpSecurity>());
725+
ApplicationContext context = getContext();
726+
return getOrApply(new CsrfConfigurer<HttpSecurity>(context));
714727
}
715728

716729
/**
@@ -917,7 +930,9 @@ public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
917930
*/
918931
public ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry requiresChannel()
919932
throws Exception {
920-
return getOrApply(new ChannelSecurityConfigurer<HttpSecurity>()).getRegistry();
933+
ApplicationContext context = getContext();
934+
return getOrApply(new ChannelSecurityConfigurer<HttpSecurity>(context))
935+
.getRegistry();
921936
}
922937

923938
/**
@@ -1241,8 +1256,16 @@ public HttpSecurity regexMatcher(String pattern) {
12411256
*/
12421257
public final class RequestMatcherConfigurer extends
12431258
AbstractRequestMatcherRegistry<RequestMatcherConfigurer> {
1259+
12441260
private List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
12451261

1262+
/**
1263+
* @param context
1264+
*/
1265+
private RequestMatcherConfigurer(ApplicationContext context) {
1266+
setApplicationContext(context);
1267+
}
1268+
12461269
protected RequestMatcherConfigurer chainRequestMatchers(
12471270
List<RequestMatcher> requestMatchers) {
12481271
matchers.addAll(requestMatchers);
@@ -1259,8 +1282,6 @@ public HttpSecurity and() {
12591282
return HttpSecurity.this;
12601283
}
12611284

1262-
private RequestMatcherConfigurer() {
1263-
}
12641285
}
12651286

12661287
/**

config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import org.apache.commons.logging.Log;
2525
import org.apache.commons.logging.LogFactory;
26+
2627
import org.springframework.beans.BeansException;
2728
import org.springframework.context.ApplicationContext;
2829
import org.springframework.context.ApplicationContextAware;
@@ -80,7 +81,7 @@ public final class WebSecurity extends
8081

8182
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();
8283

83-
private final IgnoredRequestConfigurer ignoredRequestRegistry = new IgnoredRequestConfigurer();
84+
private IgnoredRequestConfigurer ignoredRequestRegistry;
8485

8586
private FilterSecurityInterceptor filterSecurityInterceptor;
8687

@@ -316,6 +317,10 @@ protected Filter performBuild() throws Exception {
316317
public final class IgnoredRequestConfigurer extends
317318
AbstractRequestMatcherRegistry<IgnoredRequestConfigurer> {
318319

320+
private IgnoredRequestConfigurer(ApplicationContext context) {
321+
setApplicationContext(context);
322+
}
323+
319324
@Override
320325
protected IgnoredRequestConfigurer chainRequestMatchers(
321326
List<RequestMatcher> requestMatchers) {
@@ -329,13 +334,13 @@ protected IgnoredRequestConfigurer chainRequestMatchers(
329334
public WebSecurity and() {
330335
return WebSecurity.this;
331336
}
332-
333-
private IgnoredRequestConfigurer() {
334-
}
335337
}
336338

339+
@Override
337340
public void setApplicationContext(ApplicationContext applicationContext)
338341
throws BeansException {
339-
defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
342+
this.defaultWebSecurityExpressionHandler
343+
.setApplicationContext(applicationContext);
344+
this.ignoredRequestRegistry = new IgnoredRequestConfigurer(applicationContext);
340345
}
341346
}

config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,14 @@ protected void configure(HttpSecurity http) throws Exception {
329329
}
330330
// @formatter:on
331331

332+
/**
333+
* Gets the ApplicationContext
334+
* @return the context
335+
*/
336+
protected final ApplicationContext getApplicationContext() {
337+
return this.context;
338+
}
339+
332340
@Autowired
333341
public void setApplicationContext(ApplicationContext context) {
334342
this.context = context;

config/src/main/java/org/springframework/security/config/annotation/web/configurers/ChannelSecurityConfigurer.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.LinkedHashMap;
2121
import java.util.List;
2222

23+
import org.springframework.context.ApplicationContext;
2324
import org.springframework.security.access.ConfigAttribute;
2425
import org.springframework.security.access.SecurityConfig;
2526
import org.springframework.security.config.annotation.ObjectPostProcessor;
@@ -80,13 +81,14 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
8081
private LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
8182
private List<ChannelProcessor> channelProcessors;
8283

83-
private final ChannelRequestMatcherRegistry REGISTRY = new ChannelRequestMatcherRegistry();
84+
private final ChannelRequestMatcherRegistry REGISTRY;
8485

8586
/**
8687
* Creates a new instance
8788
* @see HttpSecurity#requiresChannel()
8889
*/
89-
public ChannelSecurityConfigurer() {
90+
public ChannelSecurityConfigurer(ApplicationContext context) {
91+
this.REGISTRY = new ChannelRequestMatcherRegistry(context);
9092
}
9193

9294
public ChannelRequestMatcherRegistry getRegistry() {
@@ -146,6 +148,10 @@ private ChannelRequestMatcherRegistry addAttribute(String attribute,
146148
public final class ChannelRequestMatcherRegistry extends
147149
AbstractConfigAttributeRequestMatcherRegistry<RequiresChannelUrl> {
148150

151+
private ChannelRequestMatcherRegistry(ApplicationContext context) {
152+
setApplicationContext(context);
153+
}
154+
149155
@Override
150156
protected RequiresChannelUrl chainRequestMatchersInternal(
151157
List<RequestMatcher> requestMatchers) {
@@ -185,9 +191,6 @@ public ChannelRequestMatcherRegistry channelProcessors(
185191
public H and() {
186192
return ChannelSecurityConfigurer.this.and();
187193
}
188-
189-
private ChannelRequestMatcherRegistry() {
190-
}
191194
}
192195

193196
public final class RequiresChannelUrl {

config/src/main/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurer.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import javax.servlet.http.HttpServletRequest;
2323

24+
import org.springframework.context.ApplicationContext;
2425
import org.springframework.security.access.AccessDeniedException;
2526
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
2627
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@@ -78,12 +79,14 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
7879
new HttpSessionCsrfTokenRepository());
7980
private RequestMatcher requireCsrfProtectionMatcher = CsrfFilter.DEFAULT_CSRF_MATCHER;
8081
private List<RequestMatcher> ignoredCsrfProtectionMatchers = new ArrayList<RequestMatcher>();
82+
private final ApplicationContext context;
8183

8284
/**
8385
* Creates a new instance
8486
* @see HttpSecurity#csrf()
8587
*/
86-
public CsrfConfigurer() {
88+
public CsrfConfigurer(ApplicationContext context) {
89+
this.context = context;
8790
}
8891

8992
/**
@@ -141,7 +144,8 @@ public CsrfConfigurer<H> requireCsrfProtectionMatcher(
141144
* @since 4.0
142145
*/
143146
public CsrfConfigurer<H> ignoringAntMatchers(String... antPatterns) {
144-
return new IgnoreCsrfProtectionRegistry().antMatchers(antPatterns).and();
147+
return new IgnoreCsrfProtectionRegistry(this.context).antMatchers(antPatterns)
148+
.and();
145149
}
146150

147151
@SuppressWarnings("unchecked")
@@ -265,6 +269,13 @@ private AccessDeniedHandler createAccessDeniedHandler(H http) {
265269
private class IgnoreCsrfProtectionRegistry
266270
extends AbstractRequestMatcherRegistry<IgnoreCsrfProtectionRegistry> {
267271

272+
/**
273+
* @param context
274+
*/
275+
private IgnoreCsrfProtectionRegistry(ApplicationContext context) {
276+
setApplicationContext(context);
277+
}
278+
268279
public CsrfConfigurer<H> and() {
269280
return CsrfConfigurer.this;
270281
}

0 commit comments

Comments
 (0)