SEC-2357: Fix package cycles

This commit is contained in:
Rob Winch 2013-10-14 11:15:16 -05:00
parent 14b9050616
commit 45ad74a0bd
8 changed files with 342 additions and 31 deletions

View File

@ -14,7 +14,12 @@ 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
@ -39,7 +44,13 @@ import org.springframework.util.AntPathMatcher;
* @see org.springframework.util.AntPathMatcher
*/
public final class AntPathRequestMatcher implements RequestMatcher {
private final org.springframework.security.web.util.matchers.AntPathRequestMatcher delegate;
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
@ -79,7 +90,28 @@ public final class AntPathRequestMatcher implements RequestMatcher {
* true if the matcher should consider case, else false
*/
public AntPathRequestMatcher(String pattern, String httpMethod, boolean caseSensitive) {
this.delegate = new org.springframework.security.web.util.matchers.AntPathRequestMatcher(pattern, httpMethod, 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;
}
/**
@ -89,29 +121,125 @@ public final class AntPathRequestMatcher implements RequestMatcher {
* {@code servletPath} + {@code pathInfo} of the request.
*/
public boolean matches(HttpServletRequest request) {
return this.delegate.matches(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);
}
public org.springframework.security.web.util.matchers.AntPathRequestMatcher getDelegate() {
return delegate;
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 delegate.getPattern();
return pattern;
}
public HttpMethod getHttpMethod() {
return httpMethod;
}
public boolean isCaseSensitive() {
return caseSensitive;
}
@Override
public boolean equals(Object obj) {
return delegate.equals(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;
}
@Override
public int hashCode() {
return delegate.hashCode();
int code = 31 ^ pattern.hashCode();
if (httpMethod != null) {
code ^= httpMethod.hashCode();
}
return code;
}
@Override
public String toString() {
return delegate.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) == '/');
}
}
}

View File

@ -10,19 +10,18 @@ import javax.servlet.http.HttpServletRequest;
* @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 delegate.matches(request);
return true;
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
return obj instanceof AnyRequestMatcher;
}
@Override
public int hashCode() {
return delegate.hashCode();
return 1;
}
}

View File

@ -19,6 +19,9 @@ 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;
/**
@ -36,14 +39,16 @@ import org.springframework.security.web.authentication.DelegatingAuthenticationE
*/
public class ELRequestMatcher implements RequestMatcher {
private final org.springframework.security.web.util.matchers.ELRequestMatcher delegate;
private final Expression expression;
public ELRequestMatcher(String el) {
delegate = new org.springframework.security.web.util.matchers.ELRequestMatcher(el);
SpelExpressionParser parser = new SpelExpressionParser();
expression = parser.parseExpression(el);
}
public boolean matches(HttpServletRequest request) {
return delegate.matches(request);
EvaluationContext context = createELContext(request);
return expression.getValue(context, Boolean.class).booleanValue();
}
/**
@ -52,7 +57,7 @@ public class ELRequestMatcher implements RequestMatcher {
* @return EL root context which is used to evaluate the expression
*/
public EvaluationContext createELContext(HttpServletRequest request) {
return delegate.createELContext(request);
return new StandardEvaluationContext(new ELRequestMatcherContext(request));
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2009 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;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
class ELRequestMatcherContext {
private final HttpServletRequest request;
public ELRequestMatcherContext(HttpServletRequest request) {
this.request = request;
}
public boolean hasIpAddress(String ipAddress) {
return (new IpAddressMatcher(ipAddress).matches(request));
}
public boolean hasHeader(String headerName, String value) {
String header = request.getHeader(headerName);
if (!StringUtils.hasText(header)) {
return false;
}
if (header.contains(value)) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,91 @@
package org.springframework.security.web.util;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
/**
* Matches a request based on IP Address or subnet mask matching against the remote address.
* <p>
* Both IPv6 and IPv4 addresses are supported, but a matcher which is configured with an IPv4 address will
* never match a request which returns an IPv6 address, and vice-versa.
*
* @deprecated use {@link org.springframework.security.web.util.matchers.IpAddressMatcher}
* @author Luke Taylor
* @since 3.0.2
*/
public final class IpAddressMatcher implements RequestMatcher {
private final int nMaskBits;
private final InetAddress requiredAddress;
/**
* Takes a specific IP address or a range specified using the
* IP/Netmask (e.g. 192.168.1.0/24 or 202.24.0.0/14).
*
* @param ipAddress the address or range of addresses from which the request must come.
*/
public IpAddressMatcher(String ipAddress) {
if (ipAddress.indexOf('/') > 0) {
String[] addressAndMask = StringUtils.split(ipAddress, "/");
ipAddress = addressAndMask[0];
nMaskBits = Integer.parseInt(addressAndMask[1]);
} else {
nMaskBits = -1;
}
requiredAddress = parseAddress(ipAddress);
}
public boolean matches(HttpServletRequest request) {
return matches(request.getRemoteAddr());
}
public boolean matches(String address) {
InetAddress remoteAddress = parseAddress(address);
if (!requiredAddress.getClass().equals(remoteAddress.getClass())) {
return false;
}
if (nMaskBits < 0) {
return remoteAddress.equals(requiredAddress);
}
byte[] remAddr = remoteAddress.getAddress();
byte[] reqAddr = requiredAddress.getAddress();
int oddBits = nMaskBits % 8;
int nMaskBytes = nMaskBits/8 + (oddBits == 0 ? 0 : 1);
byte[] mask = new byte[nMaskBytes];
Arrays.fill(mask, 0, oddBits == 0 ? mask.length : mask.length - 1, (byte)0xFF);
if (oddBits != 0) {
int finalByte = (1 << oddBits) - 1;
finalByte <<= 8-oddBits;
mask[mask.length - 1] = (byte) finalByte;
}
// System.out.println("Mask is " + new sun.misc.HexDumpEncoder().encode(mask));
for (int i=0; i < mask.length; i++) {
if ((remAddr[i] & mask[i]) != (reqAddr[i] & mask[i])) {
return false;
}
}
return true;
}
private InetAddress parseAddress(String address) {
try {
return InetAddress.getByName(address);
} catch (UnknownHostException e) {
throw new IllegalArgumentException("Failed to parse address" + address, e);
}
}
}

View File

@ -16,6 +16,11 @@ 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}.
*
@ -25,13 +30,17 @@ import javax.servlet.http.HttpServletRequest;
* by default. Case-insensitive matching can be used by using the constructor which takes the {@code caseInsensitive}
* argument.
*
* @deprecated use {@link org.springframework.security.web.util.matchers.RegexRequestMatcher}
*
* @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 org.springframework.security.web.util.matchers.RegexRequestMatcher delegate;
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.
@ -51,7 +60,12 @@ 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) {
this.delegate = new org.springframework.security.web.util.matchers.RegexRequestMatcher(pattern, httpMethod, 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;
}
/**
@ -62,6 +76,31 @@ public final class RegexRequestMatcher implements RequestMatcher {
* @return true if the pattern matches the URL, false otherwise.
*/
public boolean matches(HttpServletRequest request) {
return delegate.matches(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();
}
}

View File

@ -18,7 +18,6 @@ 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;
/**
@ -34,7 +33,7 @@ public class RequestMatcherEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(new ELRequestMatcher(text));
setValue(new org.springframework.security.web.util.matchers.ELRequestMatcher(text));
}
}

View File

@ -169,17 +169,19 @@ public final class AntPathRequestMatcher implements RequestMatcher {
@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();
org.springframework.security.web.util.AntPathRequestMatcher other = (org.springframework.security.web.util.AntPathRequestMatcher) obj;
return this.pattern.equals(other.getPattern()) &&
this.httpMethod == other.getHttpMethod() &&
this.caseSensitive == other.isCaseSensitive();
} else if(obj instanceof AntPathRequestMatcher) {
other = (AntPathRequestMatcher) obj;
} else {
return false;
org.springframework.security.web.util.matchers.AntPathRequestMatcher other = (AntPathRequestMatcher) obj;
return this.pattern.equals(other.pattern) &&
this.httpMethod == other.httpMethod &&
this.caseSensitive == other.caseSensitive;
}
return this.pattern.equals(other.pattern) &&
this.httpMethod == other.httpMethod &&
this.caseSensitive == other.caseSensitive;
return false;
}
@Override