Skip to content

Introduced DispatcherType request matcher #9278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@
import java.util.Arrays;
import java.util.List;

import javax.servlet.DispatcherType;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -83,7 +87,7 @@ public C anyRequest() {
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C antMatchers(HttpMethod method) {
return antMatchers(method, new String[] { "/**" });
return antMatchers(method, "/**");
}

/**
Expand Down Expand Up @@ -206,6 +210,36 @@ public C regexMatchers(String... regexPatterns) {
return chainRequestMatchers(RequestMatchers.regexMatchers(regexPatterns));
}

/**
* Maps a {@link List} of
* {@link org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher}
* instances.
* @param method the {@link HttpMethod} to use or {@code null} for any
* {@link HttpMethod}.
* @param dispatcherTypes the dispatcher types to match against
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C dispatcherTypeMatchers(@Nullable HttpMethod method, DispatcherType... dispatcherTypes) {
Assert.state(!this.anyRequestConfigured, "Can't configure dispatcherTypeMatchers after anyRequest");
List<RequestMatcher> matchers = new ArrayList<>();
for (DispatcherType dispatcherType : dispatcherTypes) {
matchers.add(new DispatcherTypeRequestMatcher(dispatcherType, method));
}
return chainRequestMatchers(matchers);
}

/**
* Create a {@link List} of
* {@link org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher}
* instances that do not specify an {@link HttpMethod}.
* @param dispatcherTypes the dispatcher types to match against
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C dispatcherTypeMatchers(DispatcherType... dispatcherTypes) {
Assert.state(!this.anyRequestConfigured, "Can't configure dispatcherTypeMatchers after anyRequest");
return dispatcherTypeMatchers(null, dispatcherTypes);
}

/**
* Associates a list of {@link RequestMatcher} instances with the
* {@link AbstractConfigAttributeRequestMatcherRegistry}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@

import java.util.List;

import javax.servlet.DispatcherType;

import org.junit.Before;
import org.junit.Test;

import org.springframework.http.HttpMethod;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

Expand Down Expand Up @@ -74,6 +77,23 @@ public void antMatchersWhenPatternParamThenReturnAntPathRequestMatcherType() {
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
}

@Test
public void dispatcherTypeMatchersWhenHttpMethodAndPatternParamsThenReturnAntPathRequestMatcherType() {
List<RequestMatcher> requestMatchers = this.matcherRegistry.dispatcherTypeMatchers(HttpMethod.GET,
DispatcherType.ASYNC);
assertThat(requestMatchers).isNotEmpty();
assertThat(requestMatchers.size()).isEqualTo(1);
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class);
}

@Test
public void dispatcherMatchersWhenPatternParamThenReturnAntPathRequestMatcherType() {
List<RequestMatcher> requestMatchers = this.matcherRegistry.dispatcherTypeMatchers(DispatcherType.INCLUDE);
assertThat(requestMatchers).isNotEmpty();
assertThat(requestMatchers.size()).isEqualTo(1);
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class);
}

private static class TestRequestMatcherRegistry extends AbstractRequestMatcherRegistry<List<RequestMatcher>> {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public AntPathRequestMatcher(String pattern, String httpMethod, boolean caseSens
@Override
public boolean matches(HttpServletRequest request) {
if (this.httpMethod != null && StringUtils.hasText(request.getMethod())
&& this.httpMethod != valueOf(request.getMethod())) {
&& this.httpMethod != HttpMethod.resolve(request.getMethod())) {
return false;
}
if (this.pattern.equals(MATCH_ALL)) {
Expand Down Expand Up @@ -211,21 +211,6 @@ public String toString() {
return sb.toString();
}

/**
* Provides a save way of obtaining the HttpMethod from a String. If the method is
* invalid, returns null.
* @param method the HTTP method to use.
* @return the HttpMethod or null if method is invalid.
*/
private static HttpMethod valueOf(String method) {
try {
return HttpMethod.valueOf(method);
}
catch (IllegalArgumentException ex) {
return null;
}
}

private interface Matcher {

boolean matches(String path);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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

import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;

import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

/**
* Checks the {@link DispatcherType} to decide whether to match a given request.
* {@code HttpServletRequest}.
*
* Can also be configured to match a specific HTTP method.
*
* @author Nick McKinney
* @since 5.5
*/
public class DispatcherTypeRequestMatcher implements RequestMatcher {

private final DispatcherType dispatcherType;

@Nullable
private final HttpMethod httpMethod;

/**
* Creates an instance which matches requests with the provided {@link DispatcherType}
* @param dispatcherType the type to match against
*/
public DispatcherTypeRequestMatcher(DispatcherType dispatcherType) {
this(dispatcherType, null);
}

/**
* Creates an instance which matches requests with the provided {@link DispatcherType}
* and {@link HttpMethod}
* @param dispatcherType the type to match against
* @param httpMethod the HTTP method to match. May be null to match all methods.
*/
public DispatcherTypeRequestMatcher(DispatcherType dispatcherType, @Nullable HttpMethod httpMethod) {
this.dispatcherType = dispatcherType;
this.httpMethod = httpMethod;
}

/**
* Performs the match against the request's method and dispatcher type.
* @param request the request to check for a match
* @return true if the http method and dispatcher type align
*/
@Override
public boolean matches(HttpServletRequest request) {
if (this.httpMethod != null && StringUtils.hasText(request.getMethod())
&& this.httpMethod != HttpMethod.resolve(request.getMethod())) {
return false;
}
return this.dispatcherType == request.getDispatcherType();
}

@Override
public String toString() {
return "DispatcherTypeRequestMatcher{" + "dispatcherType=" + this.dispatcherType + ", httpMethod="
+ this.httpMethod + '}';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ public RegexRequestMatcher(String pattern, String httpMethod, boolean caseInsens
*/
@Override
public boolean matches(HttpServletRequest request) {
if (this.httpMethod != null && request.getMethod() != null && this.httpMethod != valueOf(request.getMethod())) {
if (this.httpMethod != null && request.getMethod() != null
&& this.httpMethod != HttpMethod.resolve(request.getMethod())) {
return false;
}
String url = request.getServletPath();
Expand All @@ -101,21 +102,6 @@ public boolean matches(HttpServletRequest request) {
return this.pattern.matcher(url).matches();
}

/**
* Provides a save way of obtaining the HttpMethod from a String. If the method is
* invalid, returns null.
* @param method the HTTP method to use.
* @return the HttpMethod or null if method is invalid.
*/
private static HttpMethod valueOf(String method) {
try {
return HttpMethod.valueOf(method);
}
catch (IllegalArgumentException ex) {
return null;
}
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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

import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;

import org.junit.Test;

import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;

import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Nick McKinney
*/
public class DispatcherTypeRequestMatcherTests {

@Test
public void matches_dispatcher_type() {
HttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.GET);
DispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR);

assertThat(matcher.matches(request)).isTrue();
}

@Test
public void matches_dispatcher_type_and_http_method() {
HttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.GET);
DispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR, HttpMethod.GET);

assertThat(matcher.matches(request)).isTrue();
}

@Test
public void does_not_match_wrong_type() {
HttpServletRequest request = mockHttpServletRequest(DispatcherType.FORWARD, HttpMethod.GET);
DispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR);

assertThat(matcher.matches(request)).isFalse();
}

@Test
public void does_not_match_with_wrong_http_method() {
HttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.GET);
DispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR, HttpMethod.POST);

assertThat(matcher.matches(request)).isFalse();
}

@Test
public void null_http_method_matches_any_http_method() {
HttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.POST);
DispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR, null);

assertThat(matcher.matches(request)).isTrue();
}

private HttpServletRequest mockHttpServletRequest(DispatcherType dispatcherType, HttpMethod httpMethod) {
MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();
mockHttpServletRequest.setDispatcherType(dispatcherType);
mockHttpServletRequest.setMethod(httpMethod.name());
return mockHttpServletRequest;
}

}