Significantly enhance channel processing filter.
This commit is contained in:
parent
e555d77d4e
commit
901c7d4752
|
@ -1,4 +1,4 @@
|
||||||
Changes in version 0.5 (2004-xx-xx)
|
Changes in version 0.5 (2004-04-28)
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
* Added single sign on support via Yale Central Authentication Service (CAS)
|
* Added single sign on support via Yale Central Authentication Service (CAS)
|
||||||
|
@ -13,7 +13,9 @@ Changes in version 0.5 (2004-xx-xx)
|
||||||
* Added definable prefixes to avoid expectation of "ROLE_" GrantedAuthoritys
|
* Added definable prefixes to avoid expectation of "ROLE_" GrantedAuthoritys
|
||||||
* Added pluggable AuthenticationEntryPoints to SecurityEnforcementFilter
|
* Added pluggable AuthenticationEntryPoints to SecurityEnforcementFilter
|
||||||
* Added Apache Ant path syntax support to SecurityEnforcementFilter
|
* Added Apache Ant path syntax support to SecurityEnforcementFilter
|
||||||
|
* Added filter to automate entry into secure channels, such as HTTPS
|
||||||
* Updated JAR to Spring 1.0.1
|
* Updated JAR to Spring 1.0.1
|
||||||
|
* Updated several classes to use absolute (not relative) redirection URLs
|
||||||
* Refactored filters to use Spring application context lifecycle support
|
* Refactored filters to use Spring application context lifecycle support
|
||||||
* Improved constructor detection of nulls in User and other key objects
|
* Improved constructor detection of nulls in User and other key objects
|
||||||
* Fixed FilterInvocation.getRequestUrl() to also include getPathInfo()
|
* Fixed FilterInvocation.getRequestUrl() to also include getPathInfo()
|
||||||
|
|
|
@ -29,10 +29,11 @@ public interface ChannelDecisionManager {
|
||||||
//~ Methods ================================================================
|
//~ Methods ================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decided whether the presented {@link FilterInvocation} provides
|
* Decided whether the presented {@link FilterInvocation} provides the
|
||||||
* sufficient security based on the requested {@link
|
* appropriate level of channel security based on the requested {@link
|
||||||
* ConfigAttributeDefinition}.
|
* ConfigAttributeDefinition}.
|
||||||
*/
|
*/
|
||||||
public void decide(FilterInvocation invocation,
|
public void decide(FilterInvocation invocation,
|
||||||
ConfigAttributeDefinition config) throws SecureChannelRequiredException;
|
ConfigAttributeDefinition config)
|
||||||
|
throws InsecureChannelRequiredException, SecureChannelRequiredException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,14 +26,31 @@ import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Requires a secure channel for a web request if a {@link
|
* Ensures configuration attribute requested channel security is present by
|
||||||
* ConfigAttribute#getAttribute()} keyword is detected.
|
* review of <code>HttpServletRequest.isSecure()</code> responses.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <P>
|
* <P>
|
||||||
* The default keyword string is <Code>REQUIRES_SECURE_CHANNEL</code>, but this
|
* The class responds to two and only two case-sensitive keywords: {@link
|
||||||
* may be overriden to any value. The <code>ConfigAttribute</code> must
|
* #getInsecureKeyword()} and {@link #getSecureKeyword}. If either of these
|
||||||
* exactly match the case of the keyword string.
|
* keywords are detected, <code>HttpServletRequest.isSecure()</code> is used
|
||||||
|
* to determine the channel security offered. If the channel security differs
|
||||||
|
* from that requested by the keyword, the relevant exception is thrown.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <P>
|
||||||
|
* If both the <code>secureKeyword</code> and <code>insecureKeyword</code>
|
||||||
|
* configuration attributes are detected, the request will be deemed to be
|
||||||
|
* requesting a secure channel. This is a reasonable approach, as when in
|
||||||
|
* doubt, the decision manager assumes the most secure outcome is desired. Of
|
||||||
|
* course, you <b>should</b> indicate one configuration attribute or the other
|
||||||
|
* (not both).
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <P>
|
||||||
|
* The default <code>secureKeyword</code> and <code>insecureKeyword</code> is
|
||||||
|
* <code>REQUIRES_SECURE_CHANNEL</code> and
|
||||||
|
* <code>REQUIRES_INSECURE_CHANNEL</code> respectively.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
|
@ -43,21 +60,34 @@ public class ChannelDecisionManagerImpl implements InitializingBean,
|
||||||
ChannelDecisionManager {
|
ChannelDecisionManager {
|
||||||
//~ Instance fields ========================================================
|
//~ Instance fields ========================================================
|
||||||
|
|
||||||
private String keyword = "REQUIRES_SECURE_CHANNEL";
|
private String insecureKeyword = "REQUIRES_INSECURE_CHANNEL";
|
||||||
|
private String secureKeyword = "REQUIRES_SECURE_CHANNEL";
|
||||||
|
|
||||||
//~ Methods ================================================================
|
//~ Methods ================================================================
|
||||||
|
|
||||||
public void setKeyword(String keyword) {
|
public void setInsecureKeyword(String insecureKeyword) {
|
||||||
this.keyword = keyword;
|
this.insecureKeyword = insecureKeyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKeyword() {
|
public String getInsecureKeyword() {
|
||||||
return keyword;
|
return insecureKeyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecureKeyword(String secureKeyword) {
|
||||||
|
this.secureKeyword = secureKeyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecureKeyword() {
|
||||||
|
return secureKeyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
if ((keyword == null) || "".equals(keyword)) {
|
if ((secureKeyword == null) || "".equals(secureKeyword)) {
|
||||||
throw new IllegalArgumentException("keyword required");
|
throw new IllegalArgumentException("secureKeyword required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((insecureKeyword == null) || "".equals(insecureKeyword)) {
|
||||||
|
throw new IllegalArgumentException("insecureKeyword required");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,12 +102,19 @@ public class ChannelDecisionManagerImpl implements InitializingBean,
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
ConfigAttribute attribute = (ConfigAttribute) iter.next();
|
ConfigAttribute attribute = (ConfigAttribute) iter.next();
|
||||||
|
|
||||||
if (attribute.equals(keyword)) {
|
if (attribute.equals(secureKeyword)) {
|
||||||
if (!invocation.getHttpRequest().isSecure()) {
|
if (!invocation.getHttpRequest().isSecure()) {
|
||||||
throw new SecureChannelRequiredException(
|
throw new SecureChannelRequiredException(
|
||||||
"Request is not being made over a secure channel");
|
"Request is not being made over a secure channel");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attribute.equals(insecureKeyword)) {
|
||||||
|
if (invocation.getHttpRequest().isSecure()) {
|
||||||
|
throw new InsecureChannelRequiredException(
|
||||||
|
"Request is being made over a secure channel when an insecure channel is required");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,12 @@ import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by {@link ChannelProcessingFilter} to launch a secure web channel.
|
* Used by {@link ChannelProcessingFilter} to launch a web channel.
|
||||||
|
*
|
||||||
|
* <P>
|
||||||
|
* Depending on the implementation, a secure or insecure channel will be
|
||||||
|
* launched.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
|
@ -37,12 +42,14 @@ public interface ChannelEntryPoint {
|
||||||
* <P>
|
* <P>
|
||||||
* Implementations should modify the headers on the
|
* Implementations should modify the headers on the
|
||||||
* <code>ServletResponse</code> as necessary to commence the user agent
|
* <code>ServletResponse</code> as necessary to commence the user agent
|
||||||
* using the secure channel.
|
* using the implementation's supported channel type (ie secure or
|
||||||
|
* insecure).
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param request that resulted in a
|
* @param request that resulted in a
|
||||||
* <code>SecureChannelRequiredException</code>
|
* <code>SecureChannelRequiredException</code> or
|
||||||
* @param response so that the user agent can begin using a secure channel
|
* <code>InsecureChannelRequiredException</code>
|
||||||
|
* @param response so that the user agent can begin using a new channel
|
||||||
*/
|
*/
|
||||||
public void commence(ServletRequest request, ServletResponse response)
|
public void commence(ServletRequest request, ServletResponse response)
|
||||||
throws IOException, ServletException;
|
throws IOException, ServletException;
|
||||||
|
|
|
@ -37,7 +37,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures a request is delivered over a secure channel.
|
* Ensures a web request is delivered over the required channel.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Internally uses a {@link FilterInvocation} to represent the request, so that
|
* Internally uses a {@link FilterInvocation} to represent the request, so that
|
||||||
|
@ -62,7 +62,8 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
|
||||||
//~ Instance fields ========================================================
|
//~ Instance fields ========================================================
|
||||||
|
|
||||||
private ChannelDecisionManager channelDecisionManager;
|
private ChannelDecisionManager channelDecisionManager;
|
||||||
private ChannelEntryPoint channelEntryPoint;
|
private ChannelEntryPoint insecureChannelEntryPoint;
|
||||||
|
private ChannelEntryPoint secureChannelEntryPoint;
|
||||||
private FilterInvocationDefinitionSource filterInvocationDefinitionSource;
|
private FilterInvocationDefinitionSource filterInvocationDefinitionSource;
|
||||||
|
|
||||||
//~ Methods ================================================================
|
//~ Methods ================================================================
|
||||||
|
@ -76,14 +77,6 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
|
||||||
return channelDecisionManager;
|
return channelDecisionManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setChannelEntryPoint(ChannelEntryPoint channelEntryPoint) {
|
|
||||||
this.channelEntryPoint = channelEntryPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChannelEntryPoint getChannelEntryPoint() {
|
|
||||||
return channelEntryPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFilterInvocationDefinitionSource(
|
public void setFilterInvocationDefinitionSource(
|
||||||
FilterInvocationDefinitionSource filterInvocationDefinitionSource) {
|
FilterInvocationDefinitionSource filterInvocationDefinitionSource) {
|
||||||
this.filterInvocationDefinitionSource = filterInvocationDefinitionSource;
|
this.filterInvocationDefinitionSource = filterInvocationDefinitionSource;
|
||||||
|
@ -93,6 +86,23 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
|
||||||
return filterInvocationDefinitionSource;
|
return filterInvocationDefinitionSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setInsecureChannelEntryPoint(
|
||||||
|
ChannelEntryPoint insecureChannelEntryPoint) {
|
||||||
|
this.insecureChannelEntryPoint = insecureChannelEntryPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelEntryPoint getInsecureChannelEntryPoint() {
|
||||||
|
return insecureChannelEntryPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecureChannelEntryPoint(ChannelEntryPoint channelEntryPoint) {
|
||||||
|
this.secureChannelEntryPoint = channelEntryPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelEntryPoint getSecureChannelEntryPoint() {
|
||||||
|
return secureChannelEntryPoint;
|
||||||
|
}
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
if (filterInvocationDefinitionSource == null) {
|
if (filterInvocationDefinitionSource == null) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
|
@ -104,9 +114,14 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
|
||||||
"channelDecisionManager must be specified");
|
"channelDecisionManager must be specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channelEntryPoint == null) {
|
if (secureChannelEntryPoint == null) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"channelEntryPoint must be specified");
|
"secureChannelEntryPoint must be specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insecureChannelEntryPoint == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"insecureChannelEntryPoint must be specified");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,20 +143,32 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
|
||||||
|
|
||||||
if (attr != null) {
|
if (attr != null) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Request : " + request.toString()
|
logger.debug("Request: " + fi.getFullRequestUrl()
|
||||||
+ "; ConfigAttributes: " + attr.toString());
|
+ "; ConfigAttributes: " + attr.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
channelDecisionManager.decide(fi, attr);
|
channelDecisionManager.decide(fi, attr);
|
||||||
} catch (SecureChannelRequiredException channelException) {
|
} catch (SecureChannelRequiredException secureException) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Channel insufficient ("
|
logger.debug("Channel insufficient security ("
|
||||||
+ channelException.getMessage()
|
+ secureException.getMessage()
|
||||||
+ "); delegating to channelEntryPoint");
|
+ "); delegating to secureChannelEntryPoint");
|
||||||
}
|
}
|
||||||
|
|
||||||
channelEntryPoint.commence(request, response);
|
secureChannelEntryPoint.commence(request, response);
|
||||||
|
|
||||||
|
return;
|
||||||
|
} catch (InsecureChannelRequiredException insecureException) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Channel too much security ("
|
||||||
|
+ insecureException.getMessage()
|
||||||
|
+ "); delegating to insecureChannelEntryPoint");
|
||||||
|
}
|
||||||
|
|
||||||
|
insecureChannelEntryPoint.commence(request, response);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* Copyright 2004 Acegi Technology Pty Limited
|
||||||
|
*
|
||||||
|
* 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 net.sf.acegisecurity.securechannel;
|
||||||
|
|
||||||
|
import net.sf.acegisecurity.AccessDeniedException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown if a secure web channel is detected, but is not required.
|
||||||
|
*
|
||||||
|
* @author Ben Alex
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class InsecureChannelRequiredException extends AccessDeniedException {
|
||||||
|
//~ Constructors ===========================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an <code>InsecureChannelRequiredException</code> with the
|
||||||
|
* specified message.
|
||||||
|
*
|
||||||
|
* @param msg the detail message.
|
||||||
|
*/
|
||||||
|
public InsecureChannelRequiredException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an <code>InsecureChannelRequiredException</code> with the
|
||||||
|
* specified message and root cause.
|
||||||
|
*
|
||||||
|
* @param msg the detail message.
|
||||||
|
* @param t root cause
|
||||||
|
*/
|
||||||
|
public InsecureChannelRequiredException(String msg, Throwable t) {
|
||||||
|
super(msg, t);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* Copyright 2004 Acegi Technology Pty Limited
|
||||||
|
*
|
||||||
|
* 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 net.sf.acegisecurity.securechannel;
|
||||||
|
|
||||||
|
import net.sf.acegisecurity.util.PortMapper;
|
||||||
|
import net.sf.acegisecurity.util.PortResolver;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commences an insecure channel by retrying the original request using HTTP.
|
||||||
|
*
|
||||||
|
* <P>
|
||||||
|
* This entry point should suffice in most circumstances. However, it is not
|
||||||
|
* intended to properly handle HTTP POSTs or other usage where a standard
|
||||||
|
* redirect would cause an issue.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Ben Alex
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class RetryWithHttpEntryPoint implements InitializingBean,
|
||||||
|
ChannelEntryPoint {
|
||||||
|
//~ Static fields/initializers =============================================
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(RetryWithHttpEntryPoint.class);
|
||||||
|
|
||||||
|
//~ Instance fields ========================================================
|
||||||
|
|
||||||
|
private PortMapper portMapper;
|
||||||
|
private PortResolver portResolver;
|
||||||
|
|
||||||
|
//~ Methods ================================================================
|
||||||
|
|
||||||
|
public void setPortMapper(PortMapper portMapper) {
|
||||||
|
this.portMapper = portMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortMapper getPortMapper() {
|
||||||
|
return portMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortResolver(PortResolver portResolver) {
|
||||||
|
this.portResolver = portResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortResolver getPortResolver() {
|
||||||
|
return portResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
if (portMapper == null) {
|
||||||
|
throw new IllegalArgumentException("portMapper is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (portResolver == null) {
|
||||||
|
throw new IllegalArgumentException("portResolver is required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void commence(ServletRequest request, ServletResponse response)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
HttpServletRequest req = (HttpServletRequest) request;
|
||||||
|
|
||||||
|
String pathInfo = req.getPathInfo();
|
||||||
|
String queryString = req.getQueryString();
|
||||||
|
String contextPath = req.getContextPath();
|
||||||
|
String destination = req.getServletPath()
|
||||||
|
+ ((pathInfo == null) ? "" : pathInfo)
|
||||||
|
+ ((queryString == null) ? "" : ("?" + queryString));
|
||||||
|
|
||||||
|
String redirectUrl = contextPath;
|
||||||
|
|
||||||
|
Integer httpsPort = new Integer(portResolver.getServerPort(req));
|
||||||
|
Integer httpPort = portMapper.lookupHttpPort(httpsPort);
|
||||||
|
|
||||||
|
if (httpPort != null) {
|
||||||
|
boolean includePort = true;
|
||||||
|
|
||||||
|
if (httpPort.intValue() == 80) {
|
||||||
|
includePort = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectUrl = "http://" + req.getServerName()
|
||||||
|
+ ((includePort) ? (":" + httpPort) : "") + contextPath
|
||||||
|
+ destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Redirecting to: " + redirectUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
((HttpServletResponse) response).sendRedirect(redirectUrl);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,9 @@
|
||||||
|
|
||||||
package net.sf.acegisecurity.securechannel;
|
package net.sf.acegisecurity.securechannel;
|
||||||
|
|
||||||
|
import net.sf.acegisecurity.util.PortMapper;
|
||||||
|
import net.sf.acegisecurity.util.PortResolver;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
@ -22,10 +25,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
|
@ -53,71 +52,34 @@ public class RetryWithHttpsEntryPoint implements InitializingBean,
|
||||||
|
|
||||||
//~ Instance fields ========================================================
|
//~ Instance fields ========================================================
|
||||||
|
|
||||||
private Map httpsPortMappings;
|
private PortMapper portMapper;
|
||||||
|
private PortResolver portResolver;
|
||||||
//~ Constructors ===========================================================
|
|
||||||
|
|
||||||
public RetryWithHttpsEntryPoint() {
|
|
||||||
httpsPortMappings = new HashMap();
|
|
||||||
httpsPortMappings.put(new Integer(80), new Integer(443));
|
|
||||||
httpsPortMappings.put(new Integer(8080), new Integer(8443));
|
|
||||||
}
|
|
||||||
|
|
||||||
//~ Methods ================================================================
|
//~ Methods ================================================================
|
||||||
|
|
||||||
/**
|
public void setPortMapper(PortMapper portMapper) {
|
||||||
* <p>
|
this.portMapper = portMapper;
|
||||||
* Set to override the default http port to https port mappings of 80:443,
|
|
||||||
* and 8080:8443.
|
|
||||||
* </p>
|
|
||||||
* In a Spring XML ApplicationContext, a definition would look something
|
|
||||||
* like this:
|
|
||||||
* <pre>
|
|
||||||
* <property name="httpsPortMapping">
|
|
||||||
* <map>
|
|
||||||
* <entry key="80"><value>443</value></entry>
|
|
||||||
* <entry key="8080"><value>8443</value></entry>
|
|
||||||
* </map>
|
|
||||||
* </property>
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @param newMappings A Map consisting of String keys and String values,
|
|
||||||
* where for each entry the key is the string representation of an
|
|
||||||
* integer http port number, and the value is the string
|
|
||||||
* representation of the corresponding integer https port number.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if input map does not consist of String
|
|
||||||
* keys and values, each representing an integer port number in
|
|
||||||
* the range 1-65535 for that mapping.
|
|
||||||
*/
|
|
||||||
public void setHttpsPortMappings(HashMap newMappings) {
|
|
||||||
httpsPortMappings.clear();
|
|
||||||
|
|
||||||
Iterator it = newMappings.entrySet().iterator();
|
|
||||||
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Map.Entry entry = (Map.Entry) it.next();
|
|
||||||
Integer httpPort = new Integer((String) entry.getKey());
|
|
||||||
Integer httpsPort = new Integer((String) entry.getValue());
|
|
||||||
|
|
||||||
if ((httpPort.intValue() < 1) || (httpPort.intValue() > 65535)
|
|
||||||
|| (httpsPort.intValue() < 1) || (httpsPort.intValue() > 65535)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"one or both ports out of legal range: " + httpPort + ", "
|
|
||||||
+ httpsPort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
httpsPortMappings.put(httpPort, httpsPort);
|
public PortMapper getPortMapper() {
|
||||||
|
return portMapper;
|
||||||
|
}
|
||||||
|
|
||||||
if (httpsPortMappings.size() < 1) {
|
public void setPortResolver(PortResolver portResolver) {
|
||||||
throw new IllegalArgumentException("must map at least one port");
|
this.portResolver = portResolver;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PortResolver getPortResolver() {
|
||||||
|
return portResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
if (httpsPortMappings == null) {
|
if (portMapper == null) {
|
||||||
throw new IllegalArgumentException("httpsPortMappings required");
|
throw new IllegalArgumentException("portMapper is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (portResolver == null) {
|
||||||
|
throw new IllegalArgumentException("portResolver is required");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,25 +96,25 @@ public class RetryWithHttpsEntryPoint implements InitializingBean,
|
||||||
|
|
||||||
String redirectUrl = contextPath;
|
String redirectUrl = contextPath;
|
||||||
|
|
||||||
Integer httpPort = new Integer(req.getServerPort());
|
Integer httpPort = new Integer(portResolver.getServerPort(req));
|
||||||
Integer httpsPort = (Integer) httpsPortMappings.get(httpPort);
|
Integer httpsPort = portMapper.lookupHttpsPort(httpPort);
|
||||||
|
|
||||||
if (httpsPort != null) {
|
if (httpsPort != null) {
|
||||||
String serverName = req.getServerName();
|
boolean includePort = true;
|
||||||
redirectUrl = "https://" + serverName + ":" + httpsPort
|
|
||||||
+ contextPath + destination;
|
if (httpsPort.intValue() == 443) {
|
||||||
|
includePort = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectUrl = "https://" + req.getServerName()
|
||||||
|
+ ((includePort) ? (":" + httpsPort) : "") + contextPath
|
||||||
|
+ destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Redirecting to: " + redirectUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
((HttpServletResponse) response).sendRedirect(redirectUrl);
|
((HttpServletResponse) response).sendRedirect(redirectUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the translated (Integer -> Integer) version of the original port
|
|
||||||
* mapping specified via setHttpsPortMapping()
|
|
||||||
*
|
|
||||||
* @return DOCUMENT ME!
|
|
||||||
*/
|
|
||||||
protected Map getTranslatedHttpsPortMappings() {
|
|
||||||
return httpsPortMappings;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
/* Copyright 2004 Acegi Technology Pty Limited
|
||||||
|
*
|
||||||
|
* 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 net.sf.acegisecurity.securechannel;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import net.sf.acegisecurity.ConfigAttributeDefinition;
|
||||||
|
import net.sf.acegisecurity.MockFilterChain;
|
||||||
|
import net.sf.acegisecurity.MockHttpServletRequest;
|
||||||
|
import net.sf.acegisecurity.MockHttpServletResponse;
|
||||||
|
import net.sf.acegisecurity.SecurityConfig;
|
||||||
|
import net.sf.acegisecurity.intercept.web.FilterInvocation;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link ChannelDecisionManagerImpl}.
|
||||||
|
*
|
||||||
|
* @author Ben Alex
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class ChannelDecisionManagerImplTests extends TestCase {
|
||||||
|
//~ Methods ================================================================
|
||||||
|
|
||||||
|
public final void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
junit.textui.TestRunner.run(ChannelDecisionManagerImplTests.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsInvalidInsecureKeyword() throws Exception {
|
||||||
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
cdm.setInsecureKeyword("");
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdm.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("insecureKeyword required", expected.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
cdm.setInsecureKeyword(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdm.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("insecureKeyword required", expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsInvalidSecureKeyword() throws Exception {
|
||||||
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
cdm.setSecureKeyword("");
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdm.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("secureKeyword required", expected.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
cdm.setSecureKeyword(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdm.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("secureKeyword required", expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsNullsPassedToMainMethod() {
|
||||||
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdm.decide(null, new ConfigAttributeDefinition());
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("Nulls cannot be provided", expected.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdm.decide(new FilterInvocation(new MockHttpServletRequest("x"),
|
||||||
|
new MockHttpServletResponse(), new MockFilterChain()), null);
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("Nulls cannot be provided", expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsWhenInsecureChannelNeededAndInsecureSchemeUsed() {
|
||||||
|
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
|
||||||
|
attr.addConfigAttribute(new SecurityConfig(
|
||||||
|
"SOME_CONFIG_ATTRIBUTE_TO_IGNORE"));
|
||||||
|
attr.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("foo=bar");
|
||||||
|
request.setScheme("http");
|
||||||
|
|
||||||
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
cdm.decide(new FilterInvocation(request, new MockHttpServletResponse(),
|
||||||
|
new MockFilterChain()), attr);
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsWhenInsecureChannelNeededAndSecureSchemeUsed() {
|
||||||
|
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
|
||||||
|
attr.addConfigAttribute(new SecurityConfig(
|
||||||
|
"SOME_CONFIG_ATTRIBUTE_TO_IGNORE"));
|
||||||
|
attr.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("foo=bar");
|
||||||
|
request.setScheme("https");
|
||||||
|
|
||||||
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdm.decide(new FilterInvocation(request,
|
||||||
|
new MockHttpServletResponse(), new MockFilterChain()), attr);
|
||||||
|
} catch (InsecureChannelRequiredException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsWhenSecureChannelNeeded() {
|
||||||
|
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
|
||||||
|
attr.addConfigAttribute(new SecurityConfig(
|
||||||
|
"SOME_CONFIG_ATTRIBUTE_TO_IGNORE"));
|
||||||
|
attr.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("foo=bar");
|
||||||
|
request.setScheme("http");
|
||||||
|
|
||||||
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdm.decide(new FilterInvocation(request,
|
||||||
|
new MockHttpServletResponse(), new MockFilterChain()), attr);
|
||||||
|
} catch (SecureChannelRequiredException expected) {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetterSetters() throws Exception {
|
||||||
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
cdm.afterPropertiesSet();
|
||||||
|
assertEquals("REQUIRES_INSECURE_CHANNEL", cdm.getInsecureKeyword());
|
||||||
|
assertEquals("REQUIRES_SECURE_CHANNEL", cdm.getSecureKeyword());
|
||||||
|
|
||||||
|
cdm.setInsecureKeyword("MY_INSECURE");
|
||||||
|
cdm.setSecureKeyword("MY_SECURE");
|
||||||
|
|
||||||
|
assertEquals("MY_INSECURE", cdm.getInsecureKeyword());
|
||||||
|
assertEquals("MY_SECURE", cdm.getSecureKeyword());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIgnoresOtherConfigAttributes() {
|
||||||
|
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
|
||||||
|
attr.addConfigAttribute(new SecurityConfig("XYZ"));
|
||||||
|
|
||||||
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
cdm.decide(new FilterInvocation(new MockHttpServletRequest("x"),
|
||||||
|
new MockHttpServletResponse(), new MockFilterChain()), attr);
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
|
@ -0,0 +1,339 @@
|
||||||
|
/* Copyright 2004 Acegi Technology Pty Limited
|
||||||
|
*
|
||||||
|
* 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 net.sf.acegisecurity.securechannel;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import net.sf.acegisecurity.ConfigAttributeDefinition;
|
||||||
|
import net.sf.acegisecurity.MockFilterConfig;
|
||||||
|
import net.sf.acegisecurity.MockHttpServletRequest;
|
||||||
|
import net.sf.acegisecurity.MockHttpServletResponse;
|
||||||
|
import net.sf.acegisecurity.SecurityConfig;
|
||||||
|
import net.sf.acegisecurity.intercept.web.FilterInvocation;
|
||||||
|
import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
|
||||||
|
import net.sf.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link ChannelProcessingFilter}.
|
||||||
|
*
|
||||||
|
* @author Ben Alex
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class ChannelProcessingFilterTests extends TestCase {
|
||||||
|
//~ Methods ================================================================
|
||||||
|
|
||||||
|
public final void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
junit.textui.TestRunner.run(ChannelProcessingFilterTests.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCallsInsecureEntryPointWhenTooMuchChannelSecurity()
|
||||||
|
throws Exception {
|
||||||
|
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
|
||||||
|
attr.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
|
||||||
|
|
||||||
|
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
|
||||||
|
attr);
|
||||||
|
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
filter.setInsecureChannelEntryPoint(new MockEntryPoint(true));
|
||||||
|
filter.setSecureChannelEntryPoint(new MockEntryPoint(false));
|
||||||
|
filter.setFilterInvocationDefinitionSource(fids);
|
||||||
|
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("info=now");
|
||||||
|
request.setServletPath("/path");
|
||||||
|
request.setScheme("https");
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
MockFilterChain chain = new MockFilterChain(false);
|
||||||
|
|
||||||
|
filter.doFilter(request, response, chain);
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCallsSecureEntryPointWhenTooLittleChannelSecurity()
|
||||||
|
throws Exception {
|
||||||
|
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
|
||||||
|
attr.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
|
||||||
|
|
||||||
|
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
|
||||||
|
attr);
|
||||||
|
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
filter.setInsecureChannelEntryPoint(new MockEntryPoint(false));
|
||||||
|
filter.setSecureChannelEntryPoint(new MockEntryPoint(true));
|
||||||
|
filter.setFilterInvocationDefinitionSource(fids);
|
||||||
|
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("info=now");
|
||||||
|
request.setServletPath("/path");
|
||||||
|
request.setScheme("http");
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
MockFilterChain chain = new MockFilterChain(false);
|
||||||
|
|
||||||
|
filter.doFilter(request, response, chain);
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsMissingChannelDecisionManager()
|
||||||
|
throws Exception {
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
|
||||||
|
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
|
||||||
|
|
||||||
|
try {
|
||||||
|
filter.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("channelDecisionManager must be specified",
|
||||||
|
expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsMissingFilterInvocationDefinitionMap()
|
||||||
|
throws Exception {
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
|
||||||
|
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
|
||||||
|
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
|
||||||
|
|
||||||
|
try {
|
||||||
|
filter.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("filterInvocationDefinitionSource must be specified",
|
||||||
|
expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsMissingInsecureChannelEntryPoint()
|
||||||
|
throws Exception {
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
|
||||||
|
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
|
||||||
|
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
|
||||||
|
|
||||||
|
try {
|
||||||
|
filter.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("insecureChannelEntryPoint must be specified",
|
||||||
|
expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsMissingSecureChannelEntryPoint()
|
||||||
|
throws Exception {
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
|
||||||
|
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
|
||||||
|
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
|
||||||
|
|
||||||
|
try {
|
||||||
|
filter.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("secureChannelEntryPoint must be specified",
|
||||||
|
expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDoFilterWithNonHttpServletRequestDetected()
|
||||||
|
throws Exception {
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
|
||||||
|
try {
|
||||||
|
filter.doFilter(null, new MockHttpServletResponse(),
|
||||||
|
new MockFilterChain());
|
||||||
|
fail("Should have thrown ServletException");
|
||||||
|
} catch (ServletException expected) {
|
||||||
|
assertEquals("HttpServletRequest required", expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDoFilterWithNonHttpServletResponseDetected()
|
||||||
|
throws Exception {
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
|
||||||
|
try {
|
||||||
|
filter.doFilter(new MockHttpServletRequest(null, null), null,
|
||||||
|
new MockFilterChain());
|
||||||
|
fail("Should have thrown ServletException");
|
||||||
|
} catch (ServletException expected) {
|
||||||
|
assertEquals("HttpServletResponse required", expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDoesNotInterruptRequestsWithCorrectChannelSecurity()
|
||||||
|
throws Exception {
|
||||||
|
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
|
||||||
|
attr.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
|
||||||
|
|
||||||
|
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
|
||||||
|
attr);
|
||||||
|
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
|
||||||
|
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
|
||||||
|
filter.setFilterInvocationDefinitionSource(fids);
|
||||||
|
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("info=now");
|
||||||
|
request.setServletPath("/path");
|
||||||
|
request.setScheme("https");
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
MockFilterChain chain = new MockFilterChain(true);
|
||||||
|
|
||||||
|
filter.doFilter(request, response, chain);
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDoesNotInterruptRequestsWithNoConfigAttribute()
|
||||||
|
throws Exception {
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
|
||||||
|
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
|
||||||
|
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
|
||||||
|
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("info=now");
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
MockFilterChain chain = new MockFilterChain(true);
|
||||||
|
|
||||||
|
filter.doFilter(request, response, chain);
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetterSetters() {
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
|
||||||
|
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
|
||||||
|
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
|
||||||
|
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
|
||||||
|
|
||||||
|
assertTrue(filter.getInsecureChannelEntryPoint() != null);
|
||||||
|
assertTrue(filter.getSecureChannelEntryPoint() != null);
|
||||||
|
assertTrue(filter.getFilterInvocationDefinitionSource() != null);
|
||||||
|
assertTrue(filter.getChannelDecisionManager() != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLifecycle() throws Exception {
|
||||||
|
ChannelProcessingFilter filter = new ChannelProcessingFilter();
|
||||||
|
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
|
||||||
|
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
|
||||||
|
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
|
||||||
|
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
|
||||||
|
filter.afterPropertiesSet();
|
||||||
|
|
||||||
|
filter.init(new MockFilterConfig());
|
||||||
|
filter.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
//~ Inner Classes ==========================================================
|
||||||
|
|
||||||
|
private class MockEntryPoint implements ChannelEntryPoint {
|
||||||
|
private boolean expectToBeCalled;
|
||||||
|
|
||||||
|
public MockEntryPoint(boolean expectToBeCalled) {
|
||||||
|
this.expectToBeCalled = expectToBeCalled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MockEntryPoint() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void commence(ServletRequest request, ServletResponse response)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
if (expectToBeCalled) {
|
||||||
|
assertTrue(true);
|
||||||
|
} else {
|
||||||
|
fail("Did not expect this ChannelEntryPoint to be called");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockFilterChain implements FilterChain {
|
||||||
|
private boolean expectToProceed;
|
||||||
|
|
||||||
|
public MockFilterChain(boolean expectToProceed) {
|
||||||
|
this.expectToProceed = expectToProceed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MockFilterChain() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
if (expectToProceed) {
|
||||||
|
assertTrue(true);
|
||||||
|
} else {
|
||||||
|
fail("Did not expect filter chain to proceed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockFilterInvocationDefinitionMap
|
||||||
|
implements FilterInvocationDefinitionSource {
|
||||||
|
private ConfigAttributeDefinition toReturn;
|
||||||
|
private String servletPath;
|
||||||
|
|
||||||
|
public MockFilterInvocationDefinitionMap(String servletPath,
|
||||||
|
ConfigAttributeDefinition toReturn) {
|
||||||
|
this.servletPath = servletPath;
|
||||||
|
this.toReturn = toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MockFilterInvocationDefinitionMap() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigAttributeDefinition getAttributes(Object object)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
FilterInvocation fi = (FilterInvocation) object;
|
||||||
|
|
||||||
|
if (servletPath.equals(fi.getHttpRequest().getServletPath())) {
|
||||||
|
return toReturn;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator getConfigAttributeDefinitions() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supports(Class clazz) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
/* Copyright 2004 Acegi Technology Pty Limited
|
||||||
|
*
|
||||||
|
* 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 net.sf.acegisecurity.securechannel;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import net.sf.acegisecurity.MockHttpServletRequest;
|
||||||
|
import net.sf.acegisecurity.MockHttpServletResponse;
|
||||||
|
import net.sf.acegisecurity.MockPortResolver;
|
||||||
|
import net.sf.acegisecurity.util.PortMapperImpl;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link RetryWithHttpEntryPoint}.
|
||||||
|
*
|
||||||
|
* @author Ben Alex
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class RetryWithHttpEntryPointTests extends TestCase {
|
||||||
|
//~ Methods ================================================================
|
||||||
|
|
||||||
|
public final void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
junit.textui.TestRunner.run(RetryWithHttpEntryPointTests.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsMissingPortMapper() throws Exception {
|
||||||
|
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||||
|
ep.setPortResolver(new MockPortResolver(80, 443));
|
||||||
|
|
||||||
|
try {
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("portMapper is required", expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsMissingPortResolver() throws Exception {
|
||||||
|
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||||
|
ep.setPortMapper(new PortMapperImpl());
|
||||||
|
|
||||||
|
try {
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("portResolver is required", expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGettersSetters() {
|
||||||
|
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||||
|
ep.setPortMapper(new PortMapperImpl());
|
||||||
|
ep.setPortResolver(new MockPortResolver(8080, 8443));
|
||||||
|
assertTrue(ep.getPortMapper() != null);
|
||||||
|
assertTrue(ep.getPortResolver() != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNormalOperation() throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
|
||||||
|
request.setScheme("https");
|
||||||
|
request.setServerName("www.example.com");
|
||||||
|
request.setContextPath("/bigWebApp");
|
||||||
|
request.setServletPath("/hello");
|
||||||
|
request.setPathInfo("/pathInfo.html");
|
||||||
|
request.setServerPort(443);
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||||
|
ep.setPortMapper(new PortMapperImpl());
|
||||||
|
ep.setPortResolver(new MockPortResolver(80, 443));
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
|
||||||
|
ep.commence(request, response);
|
||||||
|
System.out.println(response.getRedirect());
|
||||||
|
assertEquals("http://www.example.com/bigWebApp/hello/pathInfo.html?open=true",
|
||||||
|
response.getRedirect());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNormalOperationWithNullPathInfoAndNullQueryString()
|
||||||
|
throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest(null);
|
||||||
|
request.setScheme("https");
|
||||||
|
request.setServerName("www.example.com");
|
||||||
|
request.setContextPath("/bigWebApp");
|
||||||
|
request.setServletPath("/hello");
|
||||||
|
request.setPathInfo(null);
|
||||||
|
request.setServerPort(443);
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||||
|
ep.setPortMapper(new PortMapperImpl());
|
||||||
|
ep.setPortResolver(new MockPortResolver(80, 443));
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
|
||||||
|
ep.commence(request, response);
|
||||||
|
System.out.println(response.getRedirect());
|
||||||
|
assertEquals("http://www.example.com/bigWebApp/hello",
|
||||||
|
response.getRedirect());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOperationWhenTargetPortIsUnknown()
|
||||||
|
throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
|
||||||
|
request.setScheme("https");
|
||||||
|
request.setServerName("www.example.com");
|
||||||
|
request.setContextPath("/bigWebApp");
|
||||||
|
request.setServletPath("/hello");
|
||||||
|
request.setPathInfo("/pathInfo.html");
|
||||||
|
request.setServerPort(8768);
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||||
|
ep.setPortMapper(new PortMapperImpl());
|
||||||
|
ep.setPortResolver(new MockPortResolver(8768, 1234));
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
|
||||||
|
ep.commence(request, response);
|
||||||
|
System.out.println(response.getRedirect());
|
||||||
|
assertEquals("/bigWebApp", response.getRedirect());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOperationWithNonStandardPort() throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
|
||||||
|
request.setScheme("https");
|
||||||
|
request.setServerName("www.example.com");
|
||||||
|
request.setContextPath("/bigWebApp");
|
||||||
|
request.setServletPath("/hello");
|
||||||
|
request.setPathInfo("/pathInfo.html");
|
||||||
|
request.setServerPort(9999);
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
PortMapperImpl portMapper = new PortMapperImpl();
|
||||||
|
Map map = new HashMap();
|
||||||
|
map.put("8888", "9999");
|
||||||
|
portMapper.setPortMappings(map);
|
||||||
|
|
||||||
|
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
|
||||||
|
ep.setPortResolver(new MockPortResolver(8888, 9999));
|
||||||
|
ep.setPortMapper(portMapper);
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
|
||||||
|
ep.commence(request, response);
|
||||||
|
System.out.println(response.getRedirect());
|
||||||
|
assertEquals("http://www.example.com:8888/bigWebApp/hello/pathInfo.html?open=true",
|
||||||
|
response.getRedirect());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
/* Copyright 2004 Acegi Technology Pty Limited
|
||||||
|
*
|
||||||
|
* 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 net.sf.acegisecurity.securechannel;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import net.sf.acegisecurity.MockHttpServletRequest;
|
||||||
|
import net.sf.acegisecurity.MockHttpServletResponse;
|
||||||
|
import net.sf.acegisecurity.MockPortResolver;
|
||||||
|
import net.sf.acegisecurity.util.PortMapperImpl;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link RetryWithHttpsEntryPoint}.
|
||||||
|
*
|
||||||
|
* @author Ben Alex
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class RetryWithHttpsEntryPointTests extends TestCase {
|
||||||
|
//~ Methods ================================================================
|
||||||
|
|
||||||
|
public final void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
junit.textui.TestRunner.run(RetryWithHttpsEntryPointTests.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsMissingPortMapper() throws Exception {
|
||||||
|
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||||
|
ep.setPortResolver(new MockPortResolver(80, 443));
|
||||||
|
|
||||||
|
try {
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("portMapper is required", expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectsMissingPortResolver() throws Exception {
|
||||||
|
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||||
|
ep.setPortMapper(new PortMapperImpl());
|
||||||
|
|
||||||
|
try {
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("portResolver is required", expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGettersSetters() {
|
||||||
|
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||||
|
ep.setPortMapper(new PortMapperImpl());
|
||||||
|
ep.setPortResolver(new MockPortResolver(8080, 8443));
|
||||||
|
assertTrue(ep.getPortMapper() != null);
|
||||||
|
assertTrue(ep.getPortResolver() != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNormalOperation() throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
|
||||||
|
request.setScheme("http");
|
||||||
|
request.setServerName("www.example.com");
|
||||||
|
request.setContextPath("/bigWebApp");
|
||||||
|
request.setServletPath("/hello");
|
||||||
|
request.setPathInfo("/pathInfo.html");
|
||||||
|
request.setServerPort(80);
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||||
|
ep.setPortMapper(new PortMapperImpl());
|
||||||
|
ep.setPortResolver(new MockPortResolver(80, 443));
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
|
||||||
|
ep.commence(request, response);
|
||||||
|
System.out.println(response.getRedirect());
|
||||||
|
assertEquals("https://www.example.com/bigWebApp/hello/pathInfo.html?open=true",
|
||||||
|
response.getRedirect());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNormalOperationWithNullPathInfoAndNullQueryString()
|
||||||
|
throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest(null);
|
||||||
|
request.setScheme("http");
|
||||||
|
request.setServerName("www.example.com");
|
||||||
|
request.setContextPath("/bigWebApp");
|
||||||
|
request.setServletPath("/hello");
|
||||||
|
request.setPathInfo(null);
|
||||||
|
request.setServerPort(80);
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||||
|
ep.setPortMapper(new PortMapperImpl());
|
||||||
|
ep.setPortResolver(new MockPortResolver(80, 443));
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
|
||||||
|
ep.commence(request, response);
|
||||||
|
System.out.println(response.getRedirect());
|
||||||
|
assertEquals("https://www.example.com/bigWebApp/hello",
|
||||||
|
response.getRedirect());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOperationWhenTargetPortIsUnknown()
|
||||||
|
throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
|
||||||
|
request.setScheme("http");
|
||||||
|
request.setServerName("www.example.com");
|
||||||
|
request.setContextPath("/bigWebApp");
|
||||||
|
request.setServletPath("/hello");
|
||||||
|
request.setPathInfo("/pathInfo.html");
|
||||||
|
request.setServerPort(8768);
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||||
|
ep.setPortMapper(new PortMapperImpl());
|
||||||
|
ep.setPortResolver(new MockPortResolver(8768, 1234));
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
|
||||||
|
ep.commence(request, response);
|
||||||
|
System.out.println(response.getRedirect());
|
||||||
|
assertEquals("/bigWebApp", response.getRedirect());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOperationWithNonStandardPort() throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
|
||||||
|
request.setScheme("http");
|
||||||
|
request.setServerName("www.example.com");
|
||||||
|
request.setContextPath("/bigWebApp");
|
||||||
|
request.setServletPath("/hello");
|
||||||
|
request.setPathInfo("/pathInfo.html");
|
||||||
|
request.setServerPort(8888);
|
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
PortMapperImpl portMapper = new PortMapperImpl();
|
||||||
|
Map map = new HashMap();
|
||||||
|
map.put("8888", "9999");
|
||||||
|
portMapper.setPortMappings(map);
|
||||||
|
|
||||||
|
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
|
||||||
|
ep.setPortResolver(new MockPortResolver(8888, 9999));
|
||||||
|
ep.setPortMapper(portMapper);
|
||||||
|
ep.afterPropertiesSet();
|
||||||
|
|
||||||
|
ep.commence(request, response);
|
||||||
|
System.out.println(response.getRedirect());
|
||||||
|
assertEquals("https://www.example.com:9999/bigWebApp/hello/pathInfo.html?open=true",
|
||||||
|
response.getRedirect());
|
||||||
|
}
|
||||||
|
}
|
|
@ -519,33 +519,23 @@
|
||||||
|
|
||||||
<para>Notice that the filter is actually a
|
<para>Notice that the filter is actually a
|
||||||
<literal>FilterToBeanProxy</literal>. Most of the filters used by the
|
<literal>FilterToBeanProxy</literal>. Most of the filters used by the
|
||||||
Acegi Security System for Spring use this class . What it does is
|
Acegi Security System for Spring use this class. Refer to the Filters
|
||||||
delegate the <literal>Filter</literal>'s methods through to a bean
|
section to learn more about this bean.</para>
|
||||||
which is obtained from the Spring application context. This enables
|
|
||||||
the bean to benefit from the Spring application context lifecycle
|
|
||||||
support and configuration flexibility.
|
|
||||||
<literal>FilterToBeanProxy</literal> only requires a single
|
|
||||||
initialization parameter, <literal>targetClass</literal> or
|
|
||||||
<literal>targetBean</literal>. The <literal>targetClass</literal>
|
|
||||||
parameter locates the first object in the application context of the
|
|
||||||
specified class, whilst <literal>targetBean</literal> locates the
|
|
||||||
object by bean name. Like standard Spring web applications, the
|
|
||||||
<literal>FilterToBeanProxy</literal> accesses the application context
|
|
||||||
via<literal>
|
|
||||||
WebApplicationContextUtils.getWebApplicationContext(ServletContext)</literal>,
|
|
||||||
so you should configure a <literal>ContextLoaderListener</literal> in
|
|
||||||
<literal>web.xml</literal>.</para>
|
|
||||||
|
|
||||||
<para>In the application context you will need to configure three
|
<para>In the application context you will need to configure four
|
||||||
beans:</para>
|
beans:</para>
|
||||||
|
|
||||||
<programlisting><bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
|
<programlisting><bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
|
||||||
<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
|
<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
|
||||||
<property name="authenticationEntryPoint"><ref bean="authenticationEntryPoint"/></property>
|
<property name="authenticationEntryPoint"><ref bean="authenticationEntryPoint"/></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="authenticationEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
|
<bean id="authenticationEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
|
||||||
<property name="loginFormUrl"><value>/acegilogin.jsp</value></property>
|
<property name="loginFormUrl"><value>/acegilogin.jsp</value></property>
|
||||||
|
<property name="forceHttps"><value>false</value></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
|
<property name="portMapper"><ref bean="portMapper"/></property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
|
<bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
|
||||||
|
@ -559,6 +549,12 @@
|
||||||
\A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_TELLER
|
\A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_TELLER
|
||||||
</value>
|
</value>
|
||||||
</property>
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- Comment the always[Scheme]Port properties to use ServletRequest.getServerPort() -->
|
||||||
|
<bean id="portResolver" class="net.sf.acegisecurity.util.PortResolverImpl">
|
||||||
|
<property name="alwaysHttpPort"><value>8080</value></property>
|
||||||
|
<property name="alwaysHttpsPort"><value>8443</value></property>
|
||||||
</bean></programlisting>
|
</bean></programlisting>
|
||||||
|
|
||||||
<para>The <literal>AuthenticationEntryPoint</literal> will be called
|
<para>The <literal>AuthenticationEntryPoint</literal> will be called
|
||||||
|
@ -577,6 +573,21 @@
|
||||||
properties related to forcing the use of HTTPS, so please refer to the
|
properties related to forcing the use of HTTPS, so please refer to the
|
||||||
JavaDocs if you require this.</para>
|
JavaDocs if you require this.</para>
|
||||||
|
|
||||||
|
<para>The <literal>PortResolver</literal> is used to inspect a HTTP
|
||||||
|
request and determine the server port it was received on. Generally
|
||||||
|
this means using <literal>ServletRequest.getServerPort()</literal>,
|
||||||
|
although implementations can be forced to always return particular
|
||||||
|
ports (based on the transport protocol), as shown in the example
|
||||||
|
above. </para>
|
||||||
|
|
||||||
|
<para>The <literal>PortMapper</literal> provides information on which
|
||||||
|
HTTPS ports correspond to which HTTP ports. This is used by the
|
||||||
|
<literal>AuthenticationProcessingFilterEntryPoint</literal> and
|
||||||
|
several other beans. The default implementation,
|
||||||
|
<literal>PortMapperImpl</literal>, knows the common HTTP ports 80 and
|
||||||
|
8080 map to HTTPS ports 443 and 8443 respectively. You can customise
|
||||||
|
this mapping if desired.</para>
|
||||||
|
|
||||||
<para>The <literal>SecurityEnforcementFilter</literal> primarily
|
<para>The <literal>SecurityEnforcementFilter</literal> primarily
|
||||||
provides session management support and initiates authentication when
|
provides session management support and initiates authentication when
|
||||||
required. It delegates actual <literal>FilterInvocation</literal>
|
required. It delegates actual <literal>FilterInvocation</literal>
|
||||||
|
@ -1585,9 +1596,8 @@ public boolean supports(Class clazz);</programlisting></para>
|
||||||
</filter-mapping></programlisting></para>
|
</filter-mapping></programlisting></para>
|
||||||
|
|
||||||
<para>For a discussion of <literal>FilterToBeanProxy</literal>, please
|
<para>For a discussion of <literal>FilterToBeanProxy</literal>, please
|
||||||
refer to the FilterInvocation Security Interceptor section. The
|
refer to the Filters section. The application context will need to
|
||||||
application context will need to define the
|
define the <literal>AuthenticationProcessingFilter</literal>:</para>
|
||||||
<literal>AuthenticationProcessingFilter</literal>:</para>
|
|
||||||
|
|
||||||
<para><programlisting><bean id="authenticationProcessingFilter" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
|
<para><programlisting><bean id="authenticationProcessingFilter" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
|
||||||
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||||
|
@ -1661,9 +1671,8 @@ public boolean supports(Class clazz);</programlisting></para>
|
||||||
</filter-mapping></programlisting></para>
|
</filter-mapping></programlisting></para>
|
||||||
|
|
||||||
<para>For a discussion of <literal>FilterToBeanProxy</literal>, please
|
<para>For a discussion of <literal>FilterToBeanProxy</literal>, please
|
||||||
refer to the FilterInvocation Security Interceptor section. The
|
refer to the Filters section. The application context will need to
|
||||||
application context will need to define the
|
define the <literal>BasicProcessingFilter</literal> and its required
|
||||||
<literal>BasicProcessingFilter</literal> and its required
|
|
||||||
collaborator:</para>
|
collaborator:</para>
|
||||||
|
|
||||||
<para><programlisting><bean id="basicProcessingFilter" class="net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter">
|
<para><programlisting><bean id="basicProcessingFilter" class="net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter">
|
||||||
|
@ -2739,6 +2748,245 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
|
||||||
</sect2>
|
</sect2>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="security-channels">
|
||||||
|
<title>Channel Security</title>
|
||||||
|
|
||||||
|
<sect2 id="security-channels-overview">
|
||||||
|
<title>Overview</title>
|
||||||
|
|
||||||
|
<para>In addition to coordinating the authentication and authorization
|
||||||
|
requirements of your application, the Acegi Security System for Spring
|
||||||
|
is also able to ensure web requests are received using an appropriate
|
||||||
|
transport. If your application has many security requirements, you'll
|
||||||
|
probably want to use HTTPS as the transport, whilst less secure pages
|
||||||
|
can use the unencrypted HTTP transport.</para>
|
||||||
|
|
||||||
|
<para>An important issue in considering transport security is that of
|
||||||
|
session hijacking. Your web container manages a
|
||||||
|
<literal>HttpSession</literal> by reference to a
|
||||||
|
<literal>jsessionid</literal> that is sent to user agents either via a
|
||||||
|
cookie or URL rewriting. If the <literal>jsessionid</literal> is ever
|
||||||
|
sent over HTTP, there is a possibility that session identifier can be
|
||||||
|
intercepted and used to impersonate the user after they complete the
|
||||||
|
authentication process. This is because most web containers maintain
|
||||||
|
the same session identifier for a given user, even after they switch
|
||||||
|
from HTTP to HTTPS pages.</para>
|
||||||
|
|
||||||
|
<para>If session hijacking is considered too significant a risk for
|
||||||
|
your particular application, the only option is to use HTTPS for every
|
||||||
|
request. This means the <literal>jsessionid</literal> is never sent
|
||||||
|
across an insecure channel. You will need to ensure your
|
||||||
|
<literal>web.xml</literal>-defined
|
||||||
|
<literal><welcome-file></literal> points to a HTTPS location,
|
||||||
|
and the application never directs the user to a HTTP location. The
|
||||||
|
Acegi Security System for Spring provides a solution to assist with
|
||||||
|
the latter.</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="security-channels-installation">
|
||||||
|
<title>Configuration</title>
|
||||||
|
|
||||||
|
<para>To utilise Acegi Security's channel security services, add the
|
||||||
|
following lines to <literal>web.xml</literal>:</para>
|
||||||
|
|
||||||
|
<para><programlisting><filter>
|
||||||
|
<filter-name>Acegi Channel Processing Filter</filter-name>
|
||||||
|
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
|
||||||
|
<init-param>
|
||||||
|
<param-name>targetClass</param-name>
|
||||||
|
<param-value>net.sf.acegisecurity.securechannel.ChannelProcessingFilter</param-value>
|
||||||
|
</init-param>
|
||||||
|
</filter>
|
||||||
|
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>Acegi Channel Processing Filter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</filter-mapping></programlisting></para>
|
||||||
|
|
||||||
|
<para>As usual when running <literal>FilterToBeanProxy</literal>, you
|
||||||
|
will also need to configure the filter in your application
|
||||||
|
context:</para>
|
||||||
|
|
||||||
|
<para><programlisting><bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter">
|
||||||
|
<property name="channelDecisionManager"><ref bean="channelDecisionManager"/></property>
|
||||||
|
<property name="secureChannelEntryPoint"><ref bean="secureChannelEntryPoint"/></property>
|
||||||
|
<property name="insecureChannelEntryPoint"><ref bean="insecureChannelEntryPoint"/></property>
|
||||||
|
<property name="filterInvocationDefinitionSource">
|
||||||
|
<value>
|
||||||
|
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
|
||||||
|
\A/secure/.*\Z=REQUIRES_SECURE_CHANNEL
|
||||||
|
\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL
|
||||||
|
\A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL
|
||||||
|
\A.*\Z=REQUIRES_INSECURE_CHANNEL
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"/>
|
||||||
|
|
||||||
|
<bean id="secureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpsEntryPoint">
|
||||||
|
<property name="portMapper"><ref bean="portMapper"/></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="insecureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpEntryPoint">
|
||||||
|
<property name="portMapper"><ref bean="portMapper"/></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
|
</bean></programlisting></para>
|
||||||
|
|
||||||
|
<para>Like <literal>FilterSecurityInterceptor</literal>, Apache Ant
|
||||||
|
style paths are also supported by the
|
||||||
|
<literal>ChannelProcessingFilter</literal>.</para>
|
||||||
|
|
||||||
|
<para>The <literal>ChannelProcessingFilter</literal> operates by
|
||||||
|
filtering all web requests and determining the configuration
|
||||||
|
attributes that apply. It then delegates to the
|
||||||
|
<literal>ChannelDecisionManager</literal>. The default implementation,
|
||||||
|
<literal>ChannelDecisionManagerImpl</literal>, should suffice in most
|
||||||
|
cases. It simply throws a
|
||||||
|
<literal>SecureChannelRequiredException</literal> or
|
||||||
|
<literal>InsecureChannelRequiredException</literal> if the request's
|
||||||
|
transport channel carries too little or too much security
|
||||||
|
respectively. </para>
|
||||||
|
|
||||||
|
<para>The <literal>ChannelProcessingFilter</literal> will detect the
|
||||||
|
<literal>SecureChannelRequiredException</literal> or
|
||||||
|
<literal>InsecureChannelRequiredException</literal> and delegate to
|
||||||
|
the <literal>secureChannelEntryPoint</literal> or
|
||||||
|
<literal>insecureChannelEntryPoint</literal> respectively. These entry
|
||||||
|
points implement the <literal>ChannelEntryPoint</literal> interface,
|
||||||
|
which allows the implementation to perform a redirect or take similar
|
||||||
|
action. The included <literal>RetryWithHttpsEntryPoint</literal> and
|
||||||
|
<literal>RetryWithHttpEntryPoint</literal> implementations simply
|
||||||
|
perform a redirect.</para>
|
||||||
|
|
||||||
|
<para>Note that the redirections are absolute (eg
|
||||||
|
http://www.company.com:8080/app/page), not relative (eg /app/page).
|
||||||
|
During testing it was discovered that Internet Explorer 6 Service Pack
|
||||||
|
1 appears to have a bug whereby it does not respond correctly to a
|
||||||
|
redirection instruction which also changes the port to use.
|
||||||
|
Accordingly, absolute URLs are used in conjunction with the
|
||||||
|
<literal>PortResolver</literal> interface to overcome this issue. The
|
||||||
|
<literal>PortResolverImpl</literal> is the included implementation,
|
||||||
|
and is capable of determining the port a request was received on
|
||||||
|
either from the <literal>ServletRequest.getServerPort()</literal>
|
||||||
|
method or from properties defined in the application context. Please
|
||||||
|
refer to the JavaDocs for <literal>PortResolverImpl</literal> for
|
||||||
|
further details.</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="security-channels-usage">
|
||||||
|
<title>Usage</title>
|
||||||
|
|
||||||
|
<para>Once configured, using the channel security filter is very easy.
|
||||||
|
Simply request pages without regard to the protocol (ie HTTP or HTTPS)
|
||||||
|
or port (eg 80, 8080, 443, 8443 etc). Obviously you'll still need a
|
||||||
|
way of making the initial request (probably via the
|
||||||
|
<literal>web.xml</literal> <literal><welcome-file></literal> or
|
||||||
|
a well-known home page URL), but once this is done the filter will
|
||||||
|
perform redirects as defined by your application context.</para>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="security-filters">
|
||||||
|
<title>Filters</title>
|
||||||
|
|
||||||
|
<sect2 id="security-filters-overview">
|
||||||
|
<title>Overview</title>
|
||||||
|
|
||||||
|
<para>The Acegi Security System for Spring uses filters extensively.
|
||||||
|
Each filter is covered in detail in a respective section of this
|
||||||
|
document. This section includes information that applies to all
|
||||||
|
filters.</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="security-filters-filtertobeanproxy">
|
||||||
|
<title>FilterToBeanProxy</title>
|
||||||
|
|
||||||
|
<para>Most filters are configured using the
|
||||||
|
<literal>FilterToBeanProxy</literal>. An example configuration from
|
||||||
|
<literal>web.xml</literal> follows:</para>
|
||||||
|
|
||||||
|
<para><programlisting><filter>
|
||||||
|
<filter-name>Acegi HTTP Request Security Filter</filter-name>
|
||||||
|
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
|
||||||
|
<init-param>
|
||||||
|
<param-name>targetClass</param-name>
|
||||||
|
<param-value>net.sf.acegisecurity.ClassThatImplementsFilter</param-value>
|
||||||
|
</init-param>
|
||||||
|
</filter></programlisting></para>
|
||||||
|
|
||||||
|
<para>Notice that the filter in <literal>web.xml</literal> is actually
|
||||||
|
a <literal>FilterToBeanProxy</literal>, and not the filter that will
|
||||||
|
actually implements the logic of the filter. What
|
||||||
|
<literal>FilterToBeanProxy</literal> does is delegate the
|
||||||
|
<literal>Filter</literal>'s methods through to a bean which is
|
||||||
|
obtained from the Spring application context. This enables the bean to
|
||||||
|
benefit from the Spring application context lifecycle support and
|
||||||
|
configuration flexibility. The bean must implement
|
||||||
|
<literal>javax.servlet.Filter</literal>.</para>
|
||||||
|
|
||||||
|
<para>The <literal>FilterToBeanProxy</literal> only requires a single
|
||||||
|
initialization parameter, <literal>targetClass</literal> or
|
||||||
|
<literal>targetBean</literal>. The <literal>targetClass</literal>
|
||||||
|
parameter locates the first object in the application context of the
|
||||||
|
specified class, whilst <literal>targetBean</literal> locates the
|
||||||
|
object by bean name. Like standard Spring web applications, the
|
||||||
|
<literal>FilterToBeanProxy</literal> accesses the application context
|
||||||
|
via<literal>
|
||||||
|
WebApplicationContextUtils.getWebApplicationContext(ServletContext)</literal>,
|
||||||
|
so you should configure a <literal>ContextLoaderListener</literal> in
|
||||||
|
<literal>web.xml</literal>.</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="security-filters-order">
|
||||||
|
<title>Filter Ordering</title>
|
||||||
|
|
||||||
|
<para>The order that filters are defined in <literal>web.xml</literal>
|
||||||
|
is important.</para>
|
||||||
|
|
||||||
|
<para>Irrespective of which filters you are actually using, the order
|
||||||
|
of the <literal><filter-mapping></literal>s should be as
|
||||||
|
follows:</para>
|
||||||
|
|
||||||
|
<orderedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>Acegi Channel Processing Filter
|
||||||
|
(<literal>ChannelProcessingFilter</literal>)</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>Acegi Authentication Processing Filter
|
||||||
|
(<literal>AuthenticationProcessingFilter</literal>)</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>Acegi CAS Processing Filter
|
||||||
|
(<literal>CasProcessingFilter</literal>)</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>Acegi HTTP BASIC Authorization Filter
|
||||||
|
(<literal>BasicProcessingFilter</literal>)</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>Acegi Security System for Spring Auto Integration Filter
|
||||||
|
(<literal>AutoIntegrationFilter</literal>)</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>Acegi HTTP Request Security Filter
|
||||||
|
(<literal>SecurityEnforcementFilter</literal>)</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist>
|
||||||
|
|
||||||
|
<para>All of the above filters use
|
||||||
|
<literal>FilterToBeanProxy</literal>, which is discussed in the
|
||||||
|
previous section.</para>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="security-sample">
|
<sect1 id="security-sample">
|
||||||
<title>Contacts Sample Application</title>
|
<title>Contacts Sample Application</title>
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,34 @@
|
||||||
|
|
||||||
<bean id="backendContactManagerTarget" class="sample.contact.ContactManagerBackend"/>
|
<bean id="backendContactManagerTarget" class="sample.contact.ContactManagerBackend"/>
|
||||||
|
|
||||||
|
<!-- ===================== HTTP CHANNEL REQUIREMENTS ==================== -->
|
||||||
|
|
||||||
|
<bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter">
|
||||||
|
<property name="channelDecisionManager"><ref bean="channelDecisionManager"/></property>
|
||||||
|
<property name="secureChannelEntryPoint"><ref bean="secureChannelEntryPoint"/></property>
|
||||||
|
<property name="insecureChannelEntryPoint"><ref bean="insecureChannelEntryPoint"/></property>
|
||||||
|
<property name="filterInvocationDefinitionSource">
|
||||||
|
<value>
|
||||||
|
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
|
||||||
|
\A/secure/.*\Z=REQUIRES_SECURE_CHANNEL
|
||||||
|
\A/j_acegi_cas_security_check.*\Z=REQUIRES_SECURE_CHANNEL
|
||||||
|
\A.*\Z=REQUIRES_INSECURE_CHANNEL
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"/>
|
||||||
|
|
||||||
|
<bean id="secureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpsEntryPoint">
|
||||||
|
<property name="portMapper"><ref bean="portMapper"/></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="insecureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpEntryPoint">
|
||||||
|
<property name="portMapper"><ref bean="portMapper"/></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- ===================== HTTP REQUEST SECURITY ==================== -->
|
<!-- ===================== HTTP REQUEST SECURITY ==================== -->
|
||||||
|
|
||||||
<bean id="casProcessingFilter" class="net.sf.acegisecurity.ui.cas.CasProcessingFilter">
|
<bean id="casProcessingFilter" class="net.sf.acegisecurity.ui.cas.CasProcessingFilter">
|
||||||
|
@ -171,6 +199,7 @@
|
||||||
<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
|
<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
|
||||||
<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
|
<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
|
||||||
<property name="authenticationEntryPoint"><ref bean="casProcessingFilterEntryPoint"/></property>
|
<property name="authenticationEntryPoint"><ref bean="casProcessingFilterEntryPoint"/></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="casProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.cas.CasProcessingFilterEntryPoint">
|
<bean id="casProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.cas.CasProcessingFilterEntryPoint">
|
||||||
|
@ -178,6 +207,14 @@
|
||||||
<property name="serviceProperties"><ref bean="serviceProperties"/></property>
|
<property name="serviceProperties"><ref bean="serviceProperties"/></property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="portMapper" class="net.sf.acegisecurity.util.PortMapperImpl"/>
|
||||||
|
|
||||||
|
<!-- Comment the always[Scheme]Port properties to use ServletRequest.getServerPort() -->
|
||||||
|
<bean id="portResolver" class="net.sf.acegisecurity.util.PortResolverImpl">
|
||||||
|
<property name="alwaysHttpPort"><value>8080</value></property>
|
||||||
|
<property name="alwaysHttpsPort"><value>8443</value></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
|
<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
|
||||||
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
|
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
|
||||||
<property name="decisionVoters">
|
<property name="decisionVoters">
|
||||||
|
|
|
@ -33,6 +33,15 @@
|
||||||
<param-value>/WEB-INF/applicationContext.xml</param-value>
|
<param-value>/WEB-INF/applicationContext.xml</param-value>
|
||||||
</context-param>
|
</context-param>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<filter-name>Acegi Channel Processing Filter</filter-name>
|
||||||
|
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
|
||||||
|
<init-param>
|
||||||
|
<param-name>targetClass</param-name>
|
||||||
|
<param-value>net.sf.acegisecurity.securechannel.ChannelProcessingFilter</param-value>
|
||||||
|
</init-param>
|
||||||
|
</filter>
|
||||||
|
|
||||||
<filter>
|
<filter>
|
||||||
<filter-name>Acegi CAS Processing Filter</filter-name>
|
<filter-name>Acegi CAS Processing Filter</filter-name>
|
||||||
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
|
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
|
||||||
|
@ -65,6 +74,11 @@
|
||||||
</init-param>
|
</init-param>
|
||||||
</filter>
|
</filter>
|
||||||
|
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>Acegi Channel Processing Filter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</filter-mapping>
|
||||||
|
|
||||||
<filter-mapping>
|
<filter-mapping>
|
||||||
<filter-name>Acegi CAS Processing Filter</filter-name>
|
<filter-name>Acegi CAS Processing Filter</filter-name>
|
||||||
<url-pattern>/*</url-pattern>
|
<url-pattern>/*</url-pattern>
|
||||||
|
|
|
@ -135,22 +135,35 @@
|
||||||
|
|
||||||
<!-- ===================== HTTP CHANNEL REQUIREMENTS ==================== -->
|
<!-- ===================== HTTP CHANNEL REQUIREMENTS ==================== -->
|
||||||
|
|
||||||
|
<!-- You will need to uncomment the "Acegi Channel Processing Filter"
|
||||||
|
<filter-mapping> in web.xml for the following beans to be used -->
|
||||||
|
|
||||||
<bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter">
|
<bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter">
|
||||||
<property name="channelDecisionManager"><ref bean="channelDecisionManager"/></property>
|
<property name="channelDecisionManager"><ref bean="channelDecisionManager"/></property>
|
||||||
<property name="channelEntryPoint"><ref bean="channelEntryPoint"/></property>
|
<property name="secureChannelEntryPoint"><ref bean="secureChannelEntryPoint"/></property>
|
||||||
|
<property name="insecureChannelEntryPoint"><ref bean="insecureChannelEntryPoint"/></property>
|
||||||
<property name="filterInvocationDefinitionSource">
|
<property name="filterInvocationDefinitionSource">
|
||||||
<value>
|
<value>
|
||||||
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
|
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
|
||||||
\A/secure/.*\Z=REQUIRES_SECURE_CHANNEL
|
\A/secure/.*\Z=REQUIRES_SECURE_CHANNEL
|
||||||
\A/info/.*\Z=REQUIRES_SECURE_CHANNEL
|
|
||||||
\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL
|
\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL
|
||||||
|
\A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL
|
||||||
|
\A.*\Z=REQUIRES_INSECURE_CHANNEL
|
||||||
</value>
|
</value>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"/>
|
<bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"/>
|
||||||
|
|
||||||
<bean id="channelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpsEntryPoint"/>
|
<bean id="secureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpsEntryPoint">
|
||||||
|
<property name="portMapper"><ref bean="portMapper"/></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="insecureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpEntryPoint">
|
||||||
|
<property name="portMapper"><ref bean="portMapper"/></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- ===================== HTTP REQUEST SECURITY ==================== -->
|
<!-- ===================== HTTP REQUEST SECURITY ==================== -->
|
||||||
|
|
||||||
|
@ -164,10 +177,22 @@
|
||||||
<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
|
<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
|
||||||
<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
|
<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
|
||||||
<property name="authenticationEntryPoint"><ref bean="authenticationProcessingFilterEntryPoint"/></property>
|
<property name="authenticationEntryPoint"><ref bean="authenticationProcessingFilterEntryPoint"/></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="authenticationProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
|
<bean id="authenticationProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
|
||||||
<property name="loginFormUrl"><value>/acegilogin.jsp</value></property>
|
<property name="loginFormUrl"><value>/acegilogin.jsp</value></property>
|
||||||
|
<property name="forceHttps"><value>false</value></property>
|
||||||
|
<property name="portMapper"><ref bean="portMapper"/></property>
|
||||||
|
<property name="portResolver"><ref bean="portResolver"/></property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="portMapper" class="net.sf.acegisecurity.util.PortMapperImpl"/>
|
||||||
|
|
||||||
|
<!-- Comment the always[Scheme]Port properties to use ServletRequest.getServerPort() -->
|
||||||
|
<bean id="portResolver" class="net.sf.acegisecurity.util.PortResolverImpl">
|
||||||
|
<property name="alwaysHttpPort"><value>8080</value></property>
|
||||||
|
<property name="alwaysHttpsPort"><value>8443</value></property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
|
<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
|
||||||
|
|
|
@ -65,10 +65,14 @@
|
||||||
</init-param>
|
</init-param>
|
||||||
</filter>
|
</filter>
|
||||||
|
|
||||||
|
<!-- Remove the comments from the following <filter-mapping> if you'd
|
||||||
|
like to ensure secure URLs are only available over HTTPS -->
|
||||||
|
<!--
|
||||||
<filter-mapping>
|
<filter-mapping>
|
||||||
<filter-name>Acegi Channel Processing Filter</filter-name>
|
<filter-name>Acegi Channel Processing Filter</filter-name>
|
||||||
<url-pattern>/*</url-pattern>
|
<url-pattern>/*</url-pattern>
|
||||||
</filter-mapping>
|
</filter-mapping>
|
||||||
|
-->
|
||||||
|
|
||||||
<filter-mapping>
|
<filter-mapping>
|
||||||
<filter-name>Acegi Authentication Processing Filter</filter-name>
|
<filter-name>Acegi Authentication Processing Filter</filter-name>
|
||||||
|
|
Loading…
Reference in New Issue