1
0
mirror of synced 2026-08-05 17:57:15 +00:00

SEC-2357: Move *RequestMatchers to .matchers package

This commit is contained in:
Rob Winch
2013-10-14 10:36:31 -05:00
parent f2b44e6beb
commit 14b9050616
62 changed files with 616 additions and 294 deletions
@@ -5,7 +5,7 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.IpAddressMatcher;
import org.springframework.security.web.util.matchers.IpAddressMatcher;
/**
*
@@ -27,7 +27,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.util.ELRequestMatcher;
import org.springframework.security.web.util.matchers.ELRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.security.web.util.RequestMatcherEditor;
import org.springframework.util.Assert;
@@ -8,7 +8,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.PortResolver;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.util.AnyRequestMatcher;
import org.springframework.security.web.util.matchers.AnyRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
/**
@@ -25,7 +25,7 @@ public class HttpSessionRequestCache implements RequestCache {
private PortResolver portResolver = new PortResolverImpl();
private boolean createSessionAllowed = true;
private RequestMatcher requestMatcher = new AnyRequestMatcher();
private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
/**
* Stores the current request, provided the configuration properties allow it.
@@ -14,12 +14,7 @@ package org.springframework.security.web.util;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpMethod;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Matcher which compares a pre-defined ant-style pattern against the URL
@@ -40,17 +35,11 @@ import org.springframework.util.StringUtils;
* @author Luke Taylor
* @author Rob Winch
* @since 3.1
*
* @deprecated use {@link org.springframework.security.web.util.matchers.AntPathRequestMatcher}
* @see org.springframework.util.AntPathMatcher
*/
public final class AntPathRequestMatcher implements RequestMatcher {
private static final Log logger = LogFactory.getLog(AntPathRequestMatcher.class);
private static final String MATCH_ALL = "/**";
private final Matcher matcher;
private final String pattern;
private final HttpMethod httpMethod;
private final boolean caseSensitive;
private final org.springframework.security.web.util.matchers.AntPathRequestMatcher delegate;
/**
* Creates a matcher with the specific pattern which will match all HTTP
@@ -90,28 +79,7 @@ public final class AntPathRequestMatcher implements RequestMatcher {
* true if the matcher should consider case, else false
*/
public AntPathRequestMatcher(String pattern, String httpMethod, boolean caseSensitive) {
Assert.hasText(pattern, "Pattern cannot be null or empty");
this.caseSensitive = caseSensitive;
if (pattern.equals(MATCH_ALL) || pattern.equals("**")) {
pattern = MATCH_ALL;
matcher = null;
} else {
if(!caseSensitive) {
pattern = pattern.toLowerCase();
}
// If the pattern ends with {@code /**} and has no other wildcards, then optimize to a sub-path match
if (pattern.endsWith(MATCH_ALL) && pattern.indexOf('?') == -1 &&
pattern.indexOf("*") == pattern.length() - 2) {
matcher = new SubpathMatcher(pattern.substring(0, pattern.length() - 3));
} else {
matcher = new SpringAntMatcher(pattern);
}
}
this.pattern = pattern;
this.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod.valueOf(httpMethod) : null;
this.delegate = new org.springframework.security.web.util.matchers.AntPathRequestMatcher(pattern, httpMethod, caseSensitive);
}
/**
@@ -121,117 +89,29 @@ public final class AntPathRequestMatcher implements RequestMatcher {
* {@code servletPath} + {@code pathInfo} of the request.
*/
public boolean matches(HttpServletRequest request) {
if (httpMethod != null && request.getMethod() != null && httpMethod != HttpMethod.valueOf(request.getMethod())) {
if (logger.isDebugEnabled()) {
logger.debug("Request '" + request.getMethod() + " " + getRequestPath(request) + "'"
+ " doesn't match '" + httpMethod + " " + pattern);
}
return false;
}
if (pattern.equals(MATCH_ALL)) {
if (logger.isDebugEnabled()) {
logger.debug("Request '" + getRequestPath(request) + "' matched by universal pattern '/**'");
}
return true;
}
String url = getRequestPath(request);
if (logger.isDebugEnabled()) {
logger.debug("Checking match of request : '" + url + "'; against '" + pattern + "'");
}
return matcher.matches(url);
return this.delegate.matches(request);
}
private String getRequestPath(HttpServletRequest request) {
String url = request.getServletPath();
if (request.getPathInfo() != null) {
url += request.getPathInfo();
}
if(!caseSensitive) {
url = url.toLowerCase();
}
return url;
public org.springframework.security.web.util.matchers.AntPathRequestMatcher getDelegate() {
return delegate;
}
public String getPattern() {
return pattern;
return delegate.getPattern();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof AntPathRequestMatcher)) {
return false;
}
AntPathRequestMatcher other = (AntPathRequestMatcher)obj;
return this.pattern.equals(other.pattern) &&
this.httpMethod == other.httpMethod &&
this.caseSensitive == other.caseSensitive;
return delegate.equals(obj);
}
@Override
public int hashCode() {
int code = 31 ^ pattern.hashCode();
if (httpMethod != null) {
code ^= httpMethod.hashCode();
}
return code;
return delegate.hashCode();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Ant [pattern='").append(pattern).append("'");
if (httpMethod != null) {
sb.append(", ").append(httpMethod);
}
sb.append("]");
return sb.toString();
}
private static interface Matcher {
boolean matches(String path);
}
private static class SpringAntMatcher implements Matcher {
private static final AntPathMatcher antMatcher = new AntPathMatcher();
private final String pattern;
private SpringAntMatcher(String pattern) {
this.pattern = pattern;
}
public boolean matches(String path) {
return antMatcher.match(pattern, path);
}
}
/**
* Optimized matcher for trailing wildcards
*/
private static class SubpathMatcher implements Matcher {
private final String subpath;
private final int length;
private SubpathMatcher(String subpath) {
assert !subpath.contains("*");
this.subpath = subpath;
this.length = subpath.length();
}
public boolean matches(String path) {
return path.startsWith(subpath) && (path.length() == length || path.charAt(length) == '/');
}
return delegate.toString();
}
}
@@ -7,20 +7,22 @@ import javax.servlet.http.HttpServletRequest;
*
* @author Luke Taylor
* @since 3.1
* @deprecated use org.springframework.security.web.util.matchers.AnyRequestMatcher.INSTANCE instead
*/
public final class AnyRequestMatcher implements RequestMatcher {
private final RequestMatcher delegate = org.springframework.security.web.util.matchers.AnyRequestMatcher.INSTANCE;
public boolean matches(HttpServletRequest request) {
return true;
return delegate.matches(request);
}
@Override
public boolean equals(Object obj) {
return obj instanceof AnyRequestMatcher;
return delegate.equals(obj);
}
@Override
public int hashCode() {
return 1;
return delegate.hashCode();
}
}
@@ -19,9 +19,6 @@ package org.springframework.security.web.util;
import javax.servlet.http.HttpServletRequest;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
/**
@@ -35,19 +32,18 @@ import org.springframework.security.web.authentication.DelegatingAuthenticationE
*
* @author Mike Wiesner
* @since 3.0.2
* @deprecated Use org.springframework.security.web.util.matchers.ELRequestMatcher
*/
public class ELRequestMatcher implements RequestMatcher {
private final Expression expression;
private final org.springframework.security.web.util.matchers.ELRequestMatcher delegate;
public ELRequestMatcher(String el) {
SpelExpressionParser parser = new SpelExpressionParser();
expression = parser.parseExpression(el);
delegate = new org.springframework.security.web.util.matchers.ELRequestMatcher(el);
}
public boolean matches(HttpServletRequest request) {
EvaluationContext context = createELContext(request);
return expression.getValue(context, Boolean.class).booleanValue();
return delegate.matches(request);
}
/**
@@ -56,7 +52,7 @@ public class ELRequestMatcher implements RequestMatcher {
* @return EL root context which is used to evaluate the expression
*/
public EvaluationContext createELContext(HttpServletRequest request) {
return new StandardEvaluationContext(new ELRequestMatcherContext(request));
return delegate.createELContext(request);
}
}
@@ -16,11 +16,6 @@ import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
/**
* Uses a regular expression to decide whether a supplied the URL of a supplied {@code HttpServletRequest}.
*
@@ -33,12 +28,10 @@ import org.springframework.util.StringUtils;
* @author Luke Taylor
* @author Rob Winch
* @since 3.1
* @deprecated use org.springframework.security.web.util.matchers.RegexRequestMatcher
*/
public final class RegexRequestMatcher implements RequestMatcher {
private final static Log logger = LogFactory.getLog(RegexRequestMatcher.class);
private final Pattern pattern;
private final HttpMethod httpMethod;
private final org.springframework.security.web.util.matchers.RegexRequestMatcher delegate;
/**
* Creates a case-sensitive {@code Pattern} instance to match against the request.
@@ -58,12 +51,7 @@ public final class RegexRequestMatcher implements RequestMatcher {
* @param caseInsensitive if true, the pattern will be compiled with the {@link Pattern#CASE_INSENSITIVE} flag set.
*/
public RegexRequestMatcher(String pattern, String httpMethod, boolean caseInsensitive) {
if (caseInsensitive) {
this.pattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
} else {
this.pattern = Pattern.compile(pattern);
}
this.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod.valueOf(httpMethod) : null;
this.delegate = new org.springframework.security.web.util.matchers.RegexRequestMatcher(pattern, httpMethod, caseInsensitive);
}
/**
@@ -74,31 +62,6 @@ public final class RegexRequestMatcher implements RequestMatcher {
* @return true if the pattern matches the URL, false otherwise.
*/
public boolean matches(HttpServletRequest request) {
if (httpMethod != null && request.getMethod() != null && httpMethod != HttpMethod.valueOf(request.getMethod())) {
return false;
}
String url = request.getServletPath();
String pathInfo = request.getPathInfo();
String query = request.getQueryString();
if (pathInfo != null || query != null) {
StringBuilder sb = new StringBuilder(url);
if (pathInfo != null) {
sb.append(pathInfo);
}
if (query != null) {
sb.append('?').append(query);
}
url = sb.toString();
}
if (logger.isDebugEnabled()) {
logger.debug("Checking match of request : '" + url + "'; against '" + pattern + "'");
}
return pattern.matcher(url).matches();
return delegate.matches(request);
}
}
@@ -18,19 +18,20 @@ package org.springframework.security.web.util;
import java.beans.PropertyEditorSupport;
import org.springframework.security.web.util.matchers.ELRequestMatcher;
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
/**
* PropertyEditor which creates ELRequestMatcher instances from Strings
*
*
* This allows to use a String in a BeanDefinition instead of an (inner) bean
* if a RequestMatcher is required, e.g. in {@link DelegatingAuthenticationEntryPoint}
*
* if a RequestMatcher is required, e.g. in {@link DelegatingAuthenticationEntryPoint}
*
* @author Mike Wiesner
* @since 3.0.2
*/
public class RequestMatcherEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(new ELRequestMatcher(text));
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import java.util.Arrays;
import java.util.List;
@@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.Assert;
@@ -0,0 +1,243 @@
/*
* Copyright 2002-2012 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
*
* http://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.matchers;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Matcher which compares a pre-defined ant-style pattern against the URL
* ({@code servletPath + pathInfo}) of an {@code HttpServletRequest}.
* The query string of the URL is ignored and matching is case-insensitive or case-sensitive depending on
* the arguments passed into the constructor.
* <p>
* Using a pattern value of {@code /**} or {@code **} is treated as a universal
* match, which will match any request. Patterns which end with {@code /**} (and have no other wildcards)
* are optimized by using a substring match &mdash; a pattern of {@code /aaa/**} will match {@code /aaa},
* {@code /aaa/} and any sub-directories, such as {@code /aaa/bbb/ccc}.
* </p>
* <p>
* For all other cases, Spring's {@link AntPathMatcher} is used to perform the match. See the Spring documentation
* for this class for comprehensive information on the syntax used.
* </p>
*
* @author Luke Taylor
* @author Rob Winch
* @since 3.1
*
* @see org.springframework.util.AntPathMatcher
*/
public final class AntPathRequestMatcher implements RequestMatcher {
private static final Log logger = LogFactory.getLog(AntPathRequestMatcher.class);
private static final String MATCH_ALL = "/**";
private final Matcher matcher;
private final String pattern;
private final HttpMethod httpMethod;
private final boolean caseSensitive;
/**
* Creates a matcher with the specific pattern which will match all HTTP
* methods in a case insensitive manner.
*
* @param pattern
* the ant pattern to use for matching
*/
public AntPathRequestMatcher(String pattern) {
this(pattern, null);
}
/**
* Creates a matcher with the supplied pattern and HTTP method in a case
* insensitive manner.
*
* @param pattern
* the ant pattern to use for matching
* @param httpMethod
* the HTTP method. The {@code matches} method will return false
* if the incoming request doesn't have the same method.
*/
public AntPathRequestMatcher(String pattern, String httpMethod) {
this(pattern,httpMethod,false);
}
/**
* Creates a matcher with the supplied pattern which will match the
* specified Http method
*
* @param pattern
* the ant pattern to use for matching
* @param httpMethod
* the HTTP method. The {@code matches} method will return false
* if the incoming request doesn't doesn't have the same method.
* @param caseSensitive
* true if the matcher should consider case, else false
*/
public AntPathRequestMatcher(String pattern, String httpMethod, boolean caseSensitive) {
Assert.hasText(pattern, "Pattern cannot be null or empty");
this.caseSensitive = caseSensitive;
if (pattern.equals(MATCH_ALL) || pattern.equals("**")) {
pattern = MATCH_ALL;
matcher = null;
} else {
if(!caseSensitive) {
pattern = pattern.toLowerCase();
}
// If the pattern ends with {@code /**} and has no other wildcards, then optimize to a sub-path match
if (pattern.endsWith(MATCH_ALL) && pattern.indexOf('?') == -1 &&
pattern.indexOf("*") == pattern.length() - 2) {
matcher = new SubpathMatcher(pattern.substring(0, pattern.length() - 3));
} else {
matcher = new SpringAntMatcher(pattern);
}
}
this.pattern = pattern;
this.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod.valueOf(httpMethod) : null;
}
/**
* Returns true if the configured pattern (and HTTP-Method) match those of the supplied request.
*
* @param request the request to match against. The ant pattern will be matched against the
* {@code servletPath} + {@code pathInfo} of the request.
*/
public boolean matches(HttpServletRequest request) {
if (httpMethod != null && request.getMethod() != null && httpMethod != HttpMethod.valueOf(request.getMethod())) {
if (logger.isDebugEnabled()) {
logger.debug("Request '" + request.getMethod() + " " + getRequestPath(request) + "'"
+ " doesn't match '" + httpMethod + " " + pattern);
}
return false;
}
if (pattern.equals(MATCH_ALL)) {
if (logger.isDebugEnabled()) {
logger.debug("Request '" + getRequestPath(request) + "' matched by universal pattern '/**'");
}
return true;
}
String url = getRequestPath(request);
if (logger.isDebugEnabled()) {
logger.debug("Checking match of request : '" + url + "'; against '" + pattern + "'");
}
return matcher.matches(url);
}
private String getRequestPath(HttpServletRequest request) {
String url = request.getServletPath();
if (request.getPathInfo() != null) {
url += request.getPathInfo();
}
if(!caseSensitive) {
url = url.toLowerCase();
}
return url;
}
public String getPattern() {
return pattern;
}
@SuppressWarnings("deprecation")
@Override
public boolean equals(Object obj) {
org.springframework.security.web.util.matchers.AntPathRequestMatcher other;
if (obj instanceof org.springframework.security.web.util.AntPathRequestMatcher) {
other = ((org.springframework.security.web.util.AntPathRequestMatcher) obj).getDelegate();
} else if(obj instanceof AntPathRequestMatcher) {
other = (AntPathRequestMatcher) obj;
} else {
return false;
}
return this.pattern.equals(other.pattern) &&
this.httpMethod == other.httpMethod &&
this.caseSensitive == other.caseSensitive;
}
@Override
public int hashCode() {
int code = 31 ^ pattern.hashCode();
if (httpMethod != null) {
code ^= httpMethod.hashCode();
}
return code;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Ant [pattern='").append(pattern).append("'");
if (httpMethod != null) {
sb.append(", ").append(httpMethod);
}
sb.append("]");
return sb.toString();
}
private static interface Matcher {
boolean matches(String path);
}
private static class SpringAntMatcher implements Matcher {
private static final AntPathMatcher antMatcher = new AntPathMatcher();
private final String pattern;
private SpringAntMatcher(String pattern) {
this.pattern = pattern;
}
public boolean matches(String path) {
return antMatcher.match(pattern, path);
}
}
/**
* Optimized matcher for trailing wildcards
*/
private static class SubpathMatcher implements Matcher {
private final String subpath;
private final int length;
private SubpathMatcher(String subpath) {
assert !subpath.contains("*");
this.subpath = subpath;
this.length = subpath.length();
}
public boolean matches(String path) {
return path.startsWith(subpath) && (path.length() == length || path.charAt(length) == '/');
}
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2002-2013 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
*
* http://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.matchers;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.util.RequestMatcher;
/**
* Matches any supplied request.
*
* @author Luke Taylor
* @since 3.1
*/
public final class AnyRequestMatcher implements RequestMatcher {
public static final RequestMatcher INSTANCE = new AnyRequestMatcher();
public boolean matches(HttpServletRequest request) {
return true;
}
@Override
@SuppressWarnings("deprecation")
public boolean equals(Object obj) {
return obj instanceof AnyRequestMatcher || obj instanceof org.springframework.security.web.util.AnyRequestMatcher;
}
@Override
public int hashCode() {
return 1;
}
private AnyRequestMatcher() {}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2010 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
*
* http://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.matchers;
import javax.servlet.http.HttpServletRequest;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
import org.springframework.security.web.util.RequestMatcher;
/**
* A RequestMatcher implementation which uses a SpEL expression
*
* <p>With the default EvaluationContext ({@link ELRequestMatcherContext}) you can use
* <code>hasIpAdress()</code> and <code>hasHeader()</code></p>
*
* <p>See {@link DelegatingAuthenticationEntryPoint} for an example configuration.</p>
*
*
* @author Mike Wiesner
* @since 3.0.2
*/
public class ELRequestMatcher implements RequestMatcher {
private final Expression expression;
public ELRequestMatcher(String el) {
SpelExpressionParser parser = new SpelExpressionParser();
expression = parser.parseExpression(el);
}
public boolean matches(HttpServletRequest request) {
EvaluationContext context = createELContext(request);
return expression.getValue(context, Boolean.class).booleanValue();
}
/**
* Subclasses can override this methode if they want to use a different EL root context
*
* @return EL root context which is used to evaluate the expression
*/
public EvaluationContext createELContext(HttpServletRequest request) {
return new StandardEvaluationContext(new ELRequestMatcherContext(request));
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import javax.servlet.http.HttpServletRequest;
@@ -1,4 +1,4 @@
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -6,6 +6,7 @@ import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.StringUtils;
/**
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import java.util.Arrays;
import java.util.Collection;
@@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.MediaType;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.accept.ContentNegotiationStrategy;
@@ -13,12 +13,13 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.Assert;
/**
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import java.util.Arrays;
import java.util.List;
@@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.Assert;
@@ -0,0 +1,105 @@
/*
* Copyright 2002-2012 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
*
* http://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.matchers;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.StringUtils;
/**
* Uses a regular expression to decide whether a supplied the URL of a supplied {@code HttpServletRequest}.
*
* Can also be configured to match a specific HTTP method.
*
* The match is performed against the {@code servletPath + pathInfo + queryString} of the request and is case-sensitive
* by default. Case-insensitive matching can be used by using the constructor which takes the {@code caseInsensitive}
* argument.
*
* @author Luke Taylor
* @author Rob Winch
* @since 3.1
*/
public final class RegexRequestMatcher implements RequestMatcher {
private final static Log logger = LogFactory.getLog(RegexRequestMatcher.class);
private final Pattern pattern;
private final HttpMethod httpMethod;
/**
* Creates a case-sensitive {@code Pattern} instance to match against the request.
*
* @param pattern the regular expression to compile into a pattern.
* @param httpMethod the HTTP method to match. May be null to match all methods.
*/
public RegexRequestMatcher(String pattern, String httpMethod) {
this(pattern, httpMethod, false);
}
/**
* As above, but allows setting of whether case-insensitive matching should be used.
*
* @param pattern the regular expression to compile into a pattern.
* @param httpMethod the HTTP method to match. May be null to match all methods.
* @param caseInsensitive if true, the pattern will be compiled with the {@link Pattern#CASE_INSENSITIVE} flag set.
*/
public RegexRequestMatcher(String pattern, String httpMethod, boolean caseInsensitive) {
if (caseInsensitive) {
this.pattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
} else {
this.pattern = Pattern.compile(pattern);
}
this.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod.valueOf(httpMethod) : null;
}
/**
* Performs the match of the request URL ({@code servletPath + pathInfo + queryString}) against
* the compiled pattern. If the query string is present, a question mark will be prepended.
*
* @param request the request to match
* @return true if the pattern matches the URL, false otherwise.
*/
public boolean matches(HttpServletRequest request) {
if (httpMethod != null && request.getMethod() != null && httpMethod != HttpMethod.valueOf(request.getMethod())) {
return false;
}
String url = request.getServletPath();
String pathInfo = request.getPathInfo();
String query = request.getQueryString();
if (pathInfo != null || query != null) {
StringBuilder sb = new StringBuilder(url);
if (pathInfo != null) {
sb.append(pathInfo);
}
if (query != null) {
sb.append('?').append(query);
}
url = sb.toString();
}
if (logger.isDebugEnabled()) {
logger.debug("Checking match of request : '" + url + "'; against '" + pattern + "'");
}
return pattern.matcher(url).matches();
}
}
@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.Assert;
/**
@@ -7,7 +7,7 @@ import org.junit.Test;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.AnyRequestMatcher;
import org.springframework.security.web.util.matchers.AnyRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
import java.util.Collection;
@@ -22,7 +22,7 @@ public class ExpressionBasedFilterInvocationSecurityMetadataSourceTests {
public void expectedAttributeIsReturned() {
final String expression = "hasRole('X')";
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
requestMap.put(new AnyRequestMatcher(), SecurityConfig.createList(expression));
requestMap.put(AnyRequestMatcher.INSTANCE, SecurityConfig.createList(expression));
ExpressionBasedFilterInvocationSecurityMetadataSource mds =
new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap, new DefaultWebSecurityExpressionHandler());
assertEquals(1, mds.getAllConfigAttributes().size());
@@ -37,7 +37,7 @@ public class ExpressionBasedFilterInvocationSecurityMetadataSourceTests {
@Test(expected=IllegalArgumentException.class)
public void invalidExpressionIsRejected() throws Exception {
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
requestMap.put(new AnyRequestMatcher(), SecurityConfig.createList("hasRole('X'"));
requestMap.put(AnyRequestMatcher.INSTANCE, SecurityConfig.createList("hasRole('X'"));
ExpressionBasedFilterInvocationSecurityMetadataSource mds =
new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap, new DefaultWebSecurityExpressionHandler());
}
@@ -29,7 +29,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.matchers.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
/**
@@ -22,7 +22,7 @@ import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.header.writers.HstsHeaderWriter;
import org.springframework.security.web.util.AnyRequestMatcher;
import org.springframework.security.web.util.matchers.AnyRequestMatcher;
/**
* @author Rob Winch
@@ -46,7 +46,7 @@ public class HstsHeaderWriterTests {
@Test
public void allArgsCustomConstructorWriteHeaders() {
request.setSecure(false);
writer = new HstsHeaderWriter(new AnyRequestMatcher(), 15768000, false);
writer = new HstsHeaderWriter(AnyRequestMatcher.INSTANCE, 15768000, false);
writer.writeHeaders(request, response);
@@ -57,7 +57,7 @@ public class HstsHeaderWriterTests {
@Test
public void maxAgeAndIncludeSubdomainsCustomConstructorWriteHeaders() {
request.setSecure(false);
writer = new HstsHeaderWriter(new AnyRequestMatcher(), 15768000, false);
writer = new HstsHeaderWriter(AnyRequestMatcher.INSTANCE, 15768000, false);
writer.writeHeaders(request, response);
@@ -124,7 +124,7 @@ public class HstsHeaderWriterTests {
@Test
public void writeHeadersAnyRequestMatcher() {
writer.setRequestMatcher(new AnyRequestMatcher());
writer.setRequestMatcher(AnyRequestMatcher.INSTANCE);
request.setSecure(false);
writer.writeHeaders(request, response);
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.when;
@@ -28,6 +28,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.security.web.util.matchers.AndRequestMatcher;
/**
*
@@ -10,7 +10,7 @@
* 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;
package org.springframework.security.web.util.matchers;
import static org.fest.assertions.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
@@ -25,6 +25,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.util.matchers.AntPathRequestMatcher;
import org.springframework.security.web.util.matchers.AnyRequestMatcher;
/**
* @author Luke Taylor
@@ -139,7 +141,7 @@ public class AntPathRequestMatcherTests {
assertEquals(new AntPathRequestMatcher("/xyz", "POST"), new AntPathRequestMatcher("/xyz", "POST"));
assertFalse(new AntPathRequestMatcher("/xyz", "POST").equals(new AntPathRequestMatcher("/xyz", "GET")));
assertFalse(new AntPathRequestMatcher("/xyz").equals(new AntPathRequestMatcher("/xxx")));
assertFalse(new AntPathRequestMatcher("/xyz").equals(new AnyRequestMatcher()));
assertFalse(new AntPathRequestMatcher("/xyz").equals(AnyRequestMatcher.INSTANCE));
assertFalse(new AntPathRequestMatcher("/xyz","GET", false).equals(new AntPathRequestMatcher("/xyz","GET", true)));
}
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.util.matchers.ELRequestMatcher;
/**
* @author Mike Wiesner
@@ -1,4 +1,4 @@
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import static org.junit.Assert.*;
@@ -6,6 +6,7 @@ import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.util.matchers.IpAddressMatcher;
/**
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import static org.fest.assertions.Assertions.assertThat;
@@ -23,6 +23,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.util.matchers.MediaTypeRequestMatcher;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
@@ -30,6 +30,7 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.util.matchers.MediaTypeRequestMatcher;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.context.request.NativeWebRequest;
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.when;
@@ -24,6 +24,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.security.web.util.matchers.NegatedRequestMatcher;
/**
*
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.when;
@@ -28,6 +28,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.security.web.util.matchers.OrRequestMatcher;
/**
*
@@ -10,7 +10,7 @@
* 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;
package org.springframework.security.web.util.matchers;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -23,6 +23,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.util.matchers.RegexRequestMatcher;
/**
* @author Luke Taylor
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.util;
package org.springframework.security.web.util.matchers;
import static org.fest.assertions.Assertions.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.util.matchers.RequestHeaderRequestMatcher;
/**
*