Provide a proxy so filters can be loaded directly from the application context.

This commit is contained in:
Ben Alex
2004-04-16 06:31:48 +00:00
parent 7b59d5f189
commit 38835da164
22 changed files with 976 additions and 862 deletions

View File

@@ -22,16 +22,10 @@ import net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.beans.factory.InitializingBean;
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -65,72 +59,79 @@ import javax.servlet.http.HttpServletResponse;
* </p>
*
* <p>
* This filter works with a <code>FilterSecurityInterceptor</code> instance. By
* default, at init time, the filter will use Spring's {@link
* WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
* method to obtain an ApplicationContext instance, inside which must be a
* configured <code>FilterSecurityInterceptor</code> instance. In the case
* where it is desireable for this filter to instantiate its own
* ApplicationContext instance from which to obtain the
* <code>FilterSecurityInterceptor</code>, the location of the config for this
* context may be specified with the optional
* <code>contextConfigLocation</code> init param.
* </p>
*
* <p>
* To use this filter, it is necessary to specify the following filter
* initialization parameters:
* </p>
*
* <ul>
* <li>
* <code>filterSecurityInterceptor</code> indicates the
* <code>FilterSecurityInterceptor</code> to delegate HTTP security decisions
* to.
* </li>
* <li>
* <code>loginFormUrl</code> indicates the URL that should be used for
* redirection if an <code>AuthenticationException</code> is detected.
* </li>
* <li>
* <code>contextConfigLocation</code> (optional, normally not used), indicates
* the path to an application context that contains a properly configured
* <code>FilterSecurityInterceptor</code>. If not specified, {@link
* WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
* will be used to obtain the context.
* </li>
* </ul>
*
* <P>
* <B>Do not use this class directly.</B> Instead configure
* <code>web.xml</code> to use the {@link
* net.sf.acegisecurity.util.FilterToBeanProxy}.
* </p>
*
* @author Ben Alex
* @author colin sampaleanu
* @version $Id$
*/
public class SecurityEnforcementFilter implements Filter {
public class SecurityEnforcementFilter implements Filter, InitializingBean {
//~ Static fields/initializers =============================================
/**
* Name of (optional) servlet filter parameter that can specify the config
* location for a new ApplicationContext used to config this filter.
*/
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
private static final Log logger = LogFactory.getLog(SecurityEnforcementFilter.class);
//~ Instance fields ========================================================
protected FilterSecurityInterceptor securityInterceptor;
protected FilterSecurityInterceptor filterSecurityInterceptor;
/**
* The URL that should be used for redirection if an
* <code>AuthenticationException</code> is detected.
*/
protected String loginFormUrl;
private ApplicationContext ctx;
private boolean ourContext = false;
//~ Methods ================================================================
public void destroy() {
if (ourContext && ctx instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) ctx).close();
public void setFilterSecurityInterceptor(
FilterSecurityInterceptor filterSecurityInterceptor) {
this.filterSecurityInterceptor = filterSecurityInterceptor;
}
public FilterSecurityInterceptor getFilterSecurityInterceptor() {
return filterSecurityInterceptor;
}
public void setLoginFormUrl(String loginFormUrl) {
this.loginFormUrl = loginFormUrl;
}
public String getLoginFormUrl() {
return loginFormUrl;
}
public void afterPropertiesSet() throws Exception {
if ((loginFormUrl == null) || "".equals(loginFormUrl)) {
throw new IllegalArgumentException("loginFormUrl must be specified");
}
if (filterSecurityInterceptor == null) {
throw new IllegalArgumentException(
"filterSecurityInterceptor must be specified");
}
}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
@@ -144,7 +145,7 @@ public class SecurityEnforcementFilter implements Filter {
FilterInvocation fi = new FilterInvocation(request, response, chain);
try {
securityInterceptor.invoke(fi);
filterSecurityInterceptor.invoke(fi);
if (logger.isDebugEnabled()) {
logger.debug("Chain processed normally");
@@ -174,49 +175,5 @@ public class SecurityEnforcementFilter implements Filter {
}
}
public void init(FilterConfig filterConfig) throws ServletException {
String appContextLocation = filterConfig.getInitParameter(CONFIG_LOCATION_PARAM);
if ((appContextLocation != null) && (appContextLocation.length() > 0)) {
ourContext = true;
if (Thread.currentThread().getContextClassLoader().getResource(appContextLocation) == null) {
throw new ServletException("Cannot locate "
+ appContextLocation);
}
}
loginFormUrl = filterConfig.getInitParameter("loginFormUrl");
if ((loginFormUrl == null) || "".equals(loginFormUrl)) {
throw new ServletException("loginFormUrl must be specified");
}
try {
if (!ourContext) {
ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(filterConfig
.getServletContext());
} else {
ctx = new ClassPathXmlApplicationContext(appContextLocation);
}
} catch (RuntimeException e) {
throw new ServletException(
"Error obtaining/creating ApplicationContext for config. Must be stored in ServletContext, or optionally '"
+ CONFIG_LOCATION_PARAM
+ "' param may be used to allow creation of new context by this filter. See root error for additional details",
e);
}
Map beans = ctx.getBeansOfType(FilterSecurityInterceptor.class, true,
true);
if (beans.size() == 0) {
throw new ServletException(
"Bean context must contain at least one bean of type FilterSecurityInterceptor");
}
String beanName = (String) beans.keySet().iterator().next();
securityInterceptor = (FilterSecurityInterceptor) beans.get(beanName);
}
public void init(FilterConfig filterConfig) throws ServletException {}
}

View File

@@ -25,16 +25,10 @@ import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.beans.factory.InitializingBean;
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -87,63 +81,44 @@ import javax.servlet.http.HttpServletResponse;
* sent with a request, it should return a 403 (forbidden) response.</I>".
* </p>
*
* <p>
* This filter works with an {@link AuthenticationManager} which is used to
* process each authentication request. By default, at init time, the filter
* will use Spring's {@link
* WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
* method to obtain an ApplicationContext instance, inside which must be a
* configured AuthenticationManager instance. In the case where it is
* desirable for this filter to instantiate its own ApplicationContext
* instance from which to obtain the AuthenticationManager, the location of
* the config for this context may be specified with the optional
* <code>contextConfigLocation</code> init param.
* <P>
* <B>Do not use this class directly.</B> Instead configure
* <code>web.xml</code> to use the {@link
* net.sf.acegisecurity.util.FilterToBeanProxy}.
* </p>
*
* <p>
* To use this filter, it is necessary to specify the following filter
* initialization parameters:
* </p>
*
* <ul>
* <li>
* <code>contextConfigLocation</code> (optional, normally not used), indicates
* the path to an application context that contains an {@link
* AuthenticationManager} which should be used to process each authentication
* request. If not specified, {@link
* WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
* will be used to obtain the context.
* </li>
* </ul>
*
*
* @author Ben Alex
* @version $Id$
*/
public class BasicProcessingFilter implements Filter {
public class BasicProcessingFilter implements Filter, InitializingBean {
//~ Static fields/initializers =============================================
/**
* Name of (optional) servlet filter parameter that can specify the config
* location for a new ApplicationContext used to config this filter.
*/
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
private static final Log logger = LogFactory.getLog(BasicProcessingFilter.class);
//~ Instance fields ========================================================
private ApplicationContext ctx;
private AuthenticationManager authenticationManager;
private boolean ourContext = false;
//~ Methods ================================================================
public void destroy() {
if (ourContext && ctx instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) ctx).close();
public void setAuthenticationManager(
AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public AuthenticationManager getAuthenticationManager() {
return authenticationManager;
}
public void afterPropertiesSet() throws Exception {
if (this.authenticationManager == null) {
throw new IllegalArgumentException(
"An AuthenticationManager is required");
}
}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
@@ -208,42 +183,5 @@ public class BasicProcessingFilter implements Filter {
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
String appContextLocation = filterConfig.getInitParameter(CONFIG_LOCATION_PARAM);
if ((appContextLocation != null) && (appContextLocation.length() > 0)) {
ourContext = true;
if (Thread.currentThread().getContextClassLoader().getResource(appContextLocation) == null) {
throw new ServletException("Cannot locate "
+ appContextLocation);
}
}
try {
if (!ourContext) {
ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(filterConfig
.getServletContext());
} else {
ctx = new ClassPathXmlApplicationContext(appContextLocation);
}
} catch (RuntimeException e) {
throw new ServletException(
"Error obtaining/creating ApplicationContext for config. Must be stored in ServletContext, or optionally '"
+ CONFIG_LOCATION_PARAM
+ "' param may be used to allow creation of new context by this filter. See root error for additional details",
e);
}
Map beans = ctx.getBeansOfType(AuthenticationManager.class, true, true);
if (beans.size() == 0) {
throw new ServletException(
"Bean context must contain at least one bean of type AuthenticationManager");
}
String beanName = (String) beans.keySet().iterator().next();
authenticationManager = (AuthenticationManager) beans.get(beanName);
}
public void init(FilterConfig arg0) throws ServletException {}
}

View File

@@ -23,16 +23,10 @@ import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.beans.factory.InitializingBean;
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -70,21 +64,7 @@ import javax.servlet.http.HttpServletResponse;
* </p>
*
* <p>
* This filter works with an {@link AuthenticationManager} which is used to
* process each authentication request. By default, at init time, the filter
* will use Spring's {@link
* WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
* method to obtain an ApplicationContext instance, inside which must be a
* configured AuthenticationManager instance. In the case where it is
* desirable for this filter to instantiate its own ApplicationContext
* instance from which to obtain the AuthenticationManager, the location of
* the config for this context may be specified with the optional
* <code>contextConfigLocation</code> init param.
* </p>
*
* <p>
* To use this filter, it is necessary to specify the following filter
* initialization parameters:
* To use this filter, it is necessary to specify the following properties:
* </p>
*
* <ul>
@@ -104,29 +84,21 @@ import javax.servlet.http.HttpServletResponse;
* respond to. This parameter is optional, and defaults to
* <code>/j_acegi_security_check</code>.
* </li>
* <li>
* <code>contextConfigLocation</code> (optional, normally not used), indicates
* the path to an application context that contains an {@link
* AuthenticationManager} which should be used to process each authentication
* request. If not specified, {@link
* WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
* will be used to obtain the context.
* </li>
* </ul>
*
* <P>
* <B>Do not use this class directly.</B> Instead configure
* <code>web.xml</code> to use the {@link
* net.sf.acegisecurity.util.FilterToBeanProxy}.
* </p>
*
* @author Ben Alex
* @author Colin Sampaleanu
* @version $Id$
*/
public class AuthenticationProcessingFilter implements Filter {
public class AuthenticationProcessingFilter implements Filter, InitializingBean {
//~ Static fields/initializers =============================================
/**
* Name of (optional) servlet filter parameter that can specify the config
* location for a new ApplicationContext used to config this filter.
*/
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
public static final String ACEGI_SECURITY_TARGET_URL_KEY = "ACEGI_SECURITY_TARGET_URL";
public static final String ACEGI_SECURITY_FORM_USERNAME_KEY = "j_username";
public static final String ACEGI_SECURITY_FORM_PASSWORD_KEY = "j_password";
@@ -135,7 +107,6 @@ public class AuthenticationProcessingFilter implements Filter {
//~ Instance fields ========================================================
private ApplicationContext ctx;
private AuthenticationManager authenticationManager;
/** Where to redirect the browser to if authentication fails */
@@ -151,17 +122,68 @@ public class AuthenticationProcessingFilter implements Filter {
* The URL destination that this filter intercepts and processes (usually
* <code>/j_acegi_security_check</code>)
*/
private String filterProcessesUrl;
private boolean ourContext = false;
private String filterProcessesUrl = "/j_acegi_security_check";
//~ Methods ================================================================
public void destroy() {
if (ourContext && ctx instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) ctx).close();
public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
this.authenticationFailureUrl = authenticationFailureUrl;
}
public String getAuthenticationFailureUrl() {
return authenticationFailureUrl;
}
public void setAuthenticationManager(
AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public AuthenticationManager getAuthenticationManager() {
return authenticationManager;
}
public void setDefaultTargetUrl(String defaultTargetUrl) {
this.defaultTargetUrl = defaultTargetUrl;
}
public String getDefaultTargetUrl() {
return defaultTargetUrl;
}
public void setFilterProcessesUrl(String filterProcessesUrl) {
this.filterProcessesUrl = filterProcessesUrl;
}
public String getFilterProcessesUrl() {
return filterProcessesUrl;
}
public void afterPropertiesSet() throws Exception {
if ((filterProcessesUrl == null) || "".equals(filterProcessesUrl)) {
throw new IllegalArgumentException(
"filterProcessesUrl must be specified");
}
if ((defaultTargetUrl == null) || "".equals(defaultTargetUrl)) {
throw new IllegalArgumentException(
"defaultTargetUrl must be specified");
}
if ((authenticationFailureUrl == null)
|| "".equals(authenticationFailureUrl)) {
throw new IllegalArgumentException(
"authenticationFailureUrl must be specified");
}
if (authenticationManager == null) {
throw new IllegalArgumentException(
"authenticationManager must be specified");
}
}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
@@ -245,63 +267,5 @@ public class AuthenticationProcessingFilter implements Filter {
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
String appContextLocation = filterConfig.getInitParameter(CONFIG_LOCATION_PARAM);
if ((appContextLocation != null) && (appContextLocation.length() > 0)) {
ourContext = true;
if (Thread.currentThread().getContextClassLoader().getResource(appContextLocation) == null) {
throw new ServletException("Cannot locate "
+ appContextLocation);
}
}
defaultTargetUrl = filterConfig.getInitParameter("defaultTargetUrl");
if ((defaultTargetUrl == null) || "".equals(defaultTargetUrl)) {
throw new ServletException("defaultTargetUrl must be specified");
}
authenticationFailureUrl = filterConfig.getInitParameter(
"authenticationFailureUrl");
if ((authenticationFailureUrl == null)
|| "".equals(authenticationFailureUrl)) {
throw new ServletException(
"authenticationFailureUrl must be specified");
}
filterProcessesUrl = filterConfig.getInitParameter("filterProcessesUrl");
if ((filterProcessesUrl == null) || "".equals(filterProcessesUrl)) {
filterProcessesUrl = "/j_acegi_security_check";
}
try {
if (!ourContext) {
ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(filterConfig
.getServletContext());
} else {
ctx = new ClassPathXmlApplicationContext(appContextLocation);
}
} catch (RuntimeException e) {
throw new ServletException(
"Error obtaining/creating ApplicationContext for config. Must be stored in ServletContext, or optionally '"
+ CONFIG_LOCATION_PARAM
+ "' param may be used to allow creation of new context by this filter. See root error for additional details",
e);
}
Map beans = ctx.getBeansOfType(AuthenticationManager.class, true, true);
if (beans.size() == 0) {
throw new ServletException(
"Bean context must contain at least one bean of type AuthenticationManager");
}
String beanName = (String) beans.keySet().iterator().next();
authenticationManager = (AuthenticationManager) beans.get(beanName);
}
public void init(FilterConfig filterConfig) throws ServletException {}
}

View File

@@ -0,0 +1,164 @@
/* 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.util;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* Delegates <code>Filter</code> requests to a Spring-managed bean.
*
* <p>
* This class acts as a proxy on behalf of a target <code>Filter</code> that is
* defined in the Spring bean context. It is necessary to specify which target
* <code>Filter</code> should be proxied as a filter initialization parameter.
* </p>
*
* <p>
* On filter initialisation, the class will use Spring's {@link
* WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
* method to obtain an <code>ApplicationContext</code> instance. It will
* expect to find the target <code>Filter</code> in this
* <code>ApplicationContext</code>.
* </p>
*
* <p>
* To use this filter, it is necessary to specify the following filter
* initialization parameters:
* </p>
*
* <ul>
* <li>
* <code>targetClass</code> indicates the class of the target
* <code>Filter</code> defined in the bean context. The only requirements are
* that this target class implements the <code>javax.servlet.Filter</code>
* interface and at least one instance is available in the
* <code>ApplicationContext</code>.
* </li>
* <li>
* <code>targetBean</code> (optional) indicates the bean name of the target
* class. This parameter should be specified if there is more than one bean in
* the <code>ApplicationContext</code> of the same type as defined by the
* <code>targetClass</code> parameter.
* </li>
* </ul>
*
*
* @author Ben Alex
* @version $Id$
*/
public class FilterToBeanProxy implements Filter {
//~ Instance fields ========================================================
private Filter delegate;
//~ Methods ================================================================
public void destroy() {
delegate.destroy();
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
delegate.doFilter(request, response, chain);
}
public void init(FilterConfig filterConfig) throws ServletException {
String targetClassString = filterConfig.getInitParameter("targetClass");
if ((targetClassString == null) || "".equals(targetClassString)) {
throw new ServletException("targetClass must be specified");
}
Class targetClass;
try {
targetClass = Thread.currentThread().getContextClassLoader()
.loadClass(targetClassString);
} catch (ClassNotFoundException ex) {
throw new ServletException("Class of type " + targetClassString
+ " not found in classloader");
}
String targetBean = filterConfig.getInitParameter("targetBean");
if ("".equals(targetBean)) {
targetBean = null;
}
ApplicationContext ctx = this.getContext(filterConfig);
Map beans = ctx.getBeansOfType(targetClass, true, true);
if (beans.size() == 0) {
throw new ServletException(
"Bean context must contain at least one bean of type "
+ targetClassString);
}
String beanName = null;
if (targetBean == null) {
// Use first bean found
beanName = (String) beans.keySet().iterator().next();
} else {
// Use the requested bean, providing it can be found
if (beans.containsKey(targetBean)) {
beanName = targetBean;
} else {
throw new ServletException("Bean with name '" + targetBean
+ "' cannot be found in bean context");
}
}
Object object = beans.get(beanName);
if (!(object instanceof Filter)) {
throw new ServletException("Bean '" + beanName
+ "' does not implement javax.servlet.Filter");
}
delegate = (Filter) object;
delegate.init(filterConfig);
}
/**
* Allows test cases to override where application context obtained from.
*
* @param filterConfig which can be used to find the
* <code>ServletContext</code>
*
* @return the Spring application context
*/
protected ApplicationContext getContext(FilterConfig filterConfig) {
return WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig
.getServletContext());
}
}

View File

@@ -0,0 +1,5 @@
<html>
<body>
General utility classes used throughout the Acegi Security System.
</body>
</html>