Initial commit.

This commit is contained in:
Ben Alex 2004-04-02 12:02:01 +00:00
parent 6ddc006012
commit 738fd2161d
25 changed files with 2572 additions and 0 deletions

View File

@ -0,0 +1,366 @@
/* 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.intercept;
import net.sf.acegisecurity.AccessDecisionManager;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationCredentialsNotFoundException;
import net.sf.acegisecurity.AuthenticationManager;
import net.sf.acegisecurity.ConfigAttribute;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import net.sf.acegisecurity.RunAsManager;
import net.sf.acegisecurity.context.Context;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.SecureContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* Abstract class that implements security interception for secure objects.
*
* <P>
* The <code>AbstractSecurityInterceptor</code> will ensure the proper startup
* configuration of the security interceptor. It will also implement the
* proper handling of secure object invocations, being:
*
* <ol>
* <li>
* Extract the {@link SecureContext} from the {@link ContextHolder}, handling
* any errors such as invalid or <code>null</code> objects.
* </li>
* <li>
* Obtain the {@link Authentication} object from the extracted
* <code>SecureContext</code>.
* </li>
* <li>
* Determine if the request relates to a secured or public invocation by
* looking up the secure object request against the {@link
* ObjectDefinitionSource}.
* </li>
* <li>
* For an invocation that is secured (there is a
* <code>ConfigAttributeDefinition</code> for the secure object invocation):
*
* <ol>
* <li>
* Authenticate the request against the configured {@link
* AuthenticationManager}, replacing the <code>Authentication</code> object on
* the <code>ContextHolder</code> with the returned value.
* </li>
* <li>
* Authorize the request against the configured {@link AccessDecisionManager}.
* </li>
* <li>
* Perform any run-as replacement via the configured {@link RunAsManager}.
* </li>
* <li>
* Perform a callback to the {@link SecurityInterceptorCallback}, which will
* actually proceed with executing the object.
* </li>
* <li>
* If the <code>RunAsManager</code> replaced the <code>Authentication</code>
* object, return the <code>ContextHolder</code> to the object that existed
* after the call to <code>AuthenticationManager</code>.
* </li>
* </ol>
*
* </li>
* <li>
* For an invocation that is public (there is no
* <code>ConfigAttributeDefinition</code> for the secure object invocation):
*
* <ol>
* <li>
* If the <code>ContextHolder</code> contains a <code>SecureContext</code>, set
* the <code>isAuthenticated</code> flag on the <code>Authentication</code>
* object to false.
* </li>
* <li>
* Perform a callback to the {@link SecurityInterceptorCallback}, which will
* actually proceed with the invocation.
* </li>
* </ol>
*
* </li>
* <li>
* Return the result from the <code>SecurityInterceptorCallback</code> to the
* method that called {@link AbstractSecurityInterceptor#interceptor(Object,
* SecurityInterceptorCallback)}. This is almost always a concrete subclass of
* the <code>AbstractSecurityInterceptor</code>.
* </li>
* </ol>
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractSecurityInterceptor implements InitializingBean {
//~ Static fields/initializers =============================================
protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class);
//~ Instance fields ========================================================
private AccessDecisionManager accessDecisionManager;
private AuthenticationManager authenticationManager;
private RunAsManager runAsManager;
private boolean validateConfigAttributes = true;
//~ Methods ================================================================
public abstract ObjectDefinitionSource obtainObjectDefinitionSource();
public void setAccessDecisionManager(
AccessDecisionManager accessDecisionManager) {
this.accessDecisionManager = accessDecisionManager;
}
public AccessDecisionManager getAccessDecisionManager() {
return accessDecisionManager;
}
public void setAuthenticationManager(AuthenticationManager newManager) {
this.authenticationManager = newManager;
}
public AuthenticationManager getAuthenticationManager() {
return this.authenticationManager;
}
public void setRunAsManager(RunAsManager runAsManager) {
this.runAsManager = runAsManager;
}
public RunAsManager getRunAsManager() {
return runAsManager;
}
public void setValidateConfigAttributes(boolean validateConfigAttributes) {
this.validateConfigAttributes = validateConfigAttributes;
}
public boolean isValidateConfigAttributes() {
return validateConfigAttributes;
}
public void afterPropertiesSet() {
if (this.authenticationManager == null) {
throw new IllegalArgumentException(
"An AuthenticationManager is required");
}
if (this.accessDecisionManager == null) {
throw new IllegalArgumentException(
"An AccessDecisionManager is required");
}
if (this.runAsManager == null) {
throw new IllegalArgumentException("A RunAsManager is required");
}
if (this.obtainObjectDefinitionSource() == null) {
throw new IllegalArgumentException(
"An ObjectDefinitionSource is required");
}
if (this.validateConfigAttributes) {
Iterator iter = this.obtainObjectDefinitionSource()
.getConfigAttributeDefinitions();
if (iter == null) {
if (logger.isWarnEnabled()) {
logger.warn(
"Could not validate configuration attributes as the MethodDefinitionSource did not return a ConfigAttributeDefinition Iterator");
}
return;
}
Set set = new HashSet();
while (iter.hasNext()) {
ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter
.next();
Iterator attributes = def.getConfigAttributes();
while (attributes.hasNext()) {
ConfigAttribute attr = (ConfigAttribute) attributes.next();
if (!this.runAsManager.supports(attr)
&& !this.accessDecisionManager.supports(attr)) {
set.add(attr);
}
}
}
if (set.size() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Validated configuration attributes");
}
} else {
throw new IllegalArgumentException(
"Unsupported configuration attributes: " + set.toString());
}
}
}
/**
* Does the work of authenticating and authorizing the request.
*
* <P>
* Throws {@link net.sf.acegisecurity.AcegiSecurityException} and its
* subclasses.
* </p>
*
* @param object details of a secure object invocation
* @param callback the object that will complete the target secure object
* invocation
*
* @return The value that was returned by the
* <code>SecurityInterceptorCallback</code>
*
* @throws Throwable if any error occurs during the
* <code>SecurityInterceptorCallback</code>
* @throws IllegalArgumentException if a required argument was missing or
* invalid
* @throws AuthenticationCredentialsNotFoundException if the
* <code>ContextHolder</code> is not populated with a valid
* <code>SecureContext</code>
*/
public Object interceptor(Object object,
SecurityInterceptorCallback callback) throws Throwable {
if (object == null) {
throw new IllegalArgumentException("Object was null");
}
if (callback == null) {
throw new IllegalArgumentException("Callback was null");
}
if (!this.obtainObjectDefinitionSource().supports(object.getClass())) {
throw new IllegalArgumentException(
"ObjectDefinitionSource does not support objects of type "
+ object.getClass());
}
ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource()
.getAttributes(object);
if (attr != null) {
if (logger.isDebugEnabled()) {
logger.debug("Secure object: " + object.toString()
+ "; ConfigAttributes: " + attr.toString());
}
// Ensure ContextHolder presents a populated SecureContext
if ((ContextHolder.getContext() == null)
|| !(ContextHolder.getContext() instanceof SecureContext)) {
throw new AuthenticationCredentialsNotFoundException(
"A valid SecureContext was not provided in the RequestContext");
}
SecureContext context = (SecureContext) ContextHolder.getContext();
// We check for just the property we're interested in (we do
// not call Context.validate() like the ContextInterceptor)
if (context.getAuthentication() == null) {
throw new AuthenticationCredentialsNotFoundException(
"Authentication credentials were not found in the SecureContext");
}
// Attempt authentication
Authentication authenticated = this.authenticationManager
.authenticate(context.getAuthentication());
authenticated.setAuthenticated(true);
logger.debug("Authenticated: " + authenticated.toString());
context.setAuthentication(authenticated);
ContextHolder.setContext((Context) context);
// Attempt authorization
this.accessDecisionManager.decide(authenticated, object, attr);
if (logger.isDebugEnabled()) {
logger.debug("Authorization successful");
}
// Attempt to run as a different user
Authentication runAs = this.runAsManager.buildRunAs(authenticated,
object, attr);
if (runAs == null) {
if (logger.isDebugEnabled()) {
logger.debug(
"RunAsManager did not change Authentication object");
}
return callback.proceedWithObject(object);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Switching to RunAs Authentication: "
+ runAs.toString());
}
context.setAuthentication(runAs);
ContextHolder.setContext((Context) context);
Object ret = callback.proceedWithObject(object);
if (logger.isDebugEnabled()) {
logger.debug("Reverting to original Authentication: "
+ authenticated.toString());
}
context.setAuthentication(authenticated);
ContextHolder.setContext((Context) context);
return ret;
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Public object - authentication not attempted");
}
// Set Authentication object (if it exists) to be unauthenticated
if ((ContextHolder.getContext() != null)
&& ContextHolder.getContext() instanceof SecureContext) {
SecureContext context = (SecureContext) ContextHolder
.getContext();
if (context.getAuthentication() != null) {
if (logger.isDebugEnabled()) {
logger.debug(
"Authentication object detected and tagged as unauthenticated");
}
Authentication authenticated = context.getAuthentication();
authenticated.setAuthenticated(false);
context.setAuthentication(authenticated);
ContextHolder.setContext((Context) context);
}
}
return callback.proceedWithObject(object);
}
}
}

View File

@ -0,0 +1,75 @@
/* 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.intercept;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import java.util.Iterator;
/**
* Implemented by classes that store and can identify the {@link
* ConfigAttributeDefinition} that applies to a given secure object
* invocation.
*
* @author Ben Alex
* @version $Id$
*/
public interface ObjectDefinitionSource {
//~ Methods ================================================================
/**
* Accesses the <code>ConfigAttributeDefinition</code> that applies to a
* given secure object.
*
* @param object the object being secured
*
* @return the <code>ConfigAttributeDefinition</code> that applies to the
* passed object
*
* @throws IllegalArgumentException if the passed object is not of a type
* supported by the <code>ObjectDefinitionSource</code>
* implementation
*/
public ConfigAttributeDefinition getAttributes(Object object)
throws IllegalArgumentException;
/**
* If available, all of the <code>ConfigAttributeDefinition</code>s defined
* by the implementing class.
*
* <P>
* This is used by the {@link AbstractSecurityInterceptor} to perform
* startup time validation of each <code>ConfigAttribute</code> configured
* against it.
* </p>
*
* @return an iterator over all the <code>ConfigAttributeDefinition</code>s
* or <code>null</code> if unsupported
*/
public Iterator getConfigAttributeDefinitions();
/**
* Indicates whether the <code>ObjectDefinitionSource</code> implementation
* is able to provide <code>ConfigAttributeDefinition</code>s for the
* indicated secure object type.
*
* @param clazz the class that is being queried
*
* @return true if the implementation can process the indicated class
*/
public boolean supports(Class clazz);
}

View File

@ -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.intercept;
/**
* Allows the {@link AbstractSecurityInterceptor} to continue the secure object
* invocation at the appropriate time.
*
* <P>
* Concrete <code>AbstractSecurityInterceptor</code> subclasses are required to
* provide a <code>SecurityInterceptorCallback</code>. This is called by the
* <code>AbstractSecurityInterceptor</code> at the exact time the secure
* object should have its processing continued. The exact way processing is
* continued is specific to the type of secure object. For example, it may
* involve proceeding with a method invocation, servicing a request, or
* continuing a filter chain.
* </p>
*
* <P>
* The result from processing the secure object should be returned to the
* <code>AbstractSecurityInterceptor</code>, which in turn will ultimately
* return it to the calling class.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface SecurityInterceptorCallback {
//~ Methods ================================================================
/**
* Continues to process the secured object.
*
* @return the result (if any) from calling the secured object
*/
public Object proceedWithObject(Object object) throws Throwable;
}

View File

@ -0,0 +1,73 @@
/* 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.intercept.method;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Abstract implementation of <Code>MethodDefinitionSource</code>.
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractMethodDefinitionSource
implements MethodDefinitionSource {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(AbstractMethodDefinitionSource.class);
//~ Methods ================================================================
public ConfigAttributeDefinition getAttributes(Object object)
throws IllegalArgumentException {
if ((object == null) || !this.supports(object.getClass())) {
throw new IllegalArgumentException(
"Object must be a MethodInvocation");
}
return this.lookupAttributes((MethodInvocation) object);
}
public boolean supports(Class clazz) {
if (MethodInvocation.class.isAssignableFrom(clazz)) {
return true;
} else {
return false;
}
}
/**
* Performs the actual lookup of the relevant
* <code>ConfigAttributeDefinition</code> for the specified
* <code>MethodInvocation</code>.
*
* <P>
* Provided so subclasses need only to provide one basic method to properly
* interface with the <code>MethodDefinitionSource</code>.
* </p>
*
* @return the <code>ConfigAttributeDefinition</code> that applies to the
* specified <code>MethodInvocation</code>
*/
protected abstract ConfigAttributeDefinition lookupAttributes(
MethodInvocation mi);
}

View File

@ -0,0 +1,162 @@
/* 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.intercept.method;
import net.sf.acegisecurity.ConfigAttribute;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.metadata.Attributes;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
/**
* Stores a {@link ConfigAttributeDefinition} for each method signature defined
* by Commons Attributes.
*
* <P>
* This class will only detect those attributes which are defined for:
*
* <ul>
* <li>
* The class-wide attributes defined for the intercepted class.
* </li>
* <li>
* The class-wide attributes defined for interfaces explicitly implemented by
* the intercepted class.
* </li>
* <li>
* The method-specific attributes defined for the intercepted method of the
* intercepted class.
* </li>
* <li>
* The method-specific attributes defined by any explicitly implemented
* interface if that interface contains a method signature matching that of
* the intercepted method.
* </li>
* </ul>
* </p>
*
* <P>
* Note that attributes defined against parent classes (either for their
* methods or interfaces) are not detected. The attributes must be defined
* against an explicit method or interface on the intercepted class.
* </p>
*
* <p>
* Attributes detected that do not implement {@link ConfigAttribute} will be
* ignored.
* </p>
*
* @author Cameron Braid
* @author Ben Alex
* @version $Id$
*/
public class MethodDefinitionAttributes extends AbstractMethodDefinitionSource {
//~ Instance fields ========================================================
private Attributes attributes;
//~ Methods ================================================================
public void setAttributes(Attributes attributes) {
this.attributes = attributes;
}
public Iterator getConfigAttributeDefinitions() {
return null;
}
protected ConfigAttributeDefinition lookupAttributes(
MethodInvocation invocation) {
ConfigAttributeDefinition definition = new ConfigAttributeDefinition();
Class interceptedClass = invocation.getMethod().getDeclaringClass();
// add the class level attributes for the implementing class
addClassAttributes(definition, interceptedClass);
// add the class level attributes for the implemented interfaces
addClassAttributes(definition, interceptedClass.getInterfaces());
// add the method level attributes for the implemented method
addMethodAttributes(definition, invocation.getMethod());
// add the method level attributes for the implemented intreface methods
addInterfaceMethodAttributes(definition, invocation.getMethod());
return definition;
}
private void add(ConfigAttributeDefinition definition, Collection attribs) {
for (Iterator iter = attribs.iterator(); iter.hasNext();) {
Object o = (Object) iter.next();
if (o instanceof ConfigAttribute) {
definition.addConfigAttribute((ConfigAttribute) o);
}
}
}
private void addClassAttributes(ConfigAttributeDefinition definition,
Class clazz) {
addClassAttributes(definition, new Class[] {clazz});
}
private void addClassAttributes(ConfigAttributeDefinition definition,
Class[] clazz) {
for (int i = 0; i < clazz.length; i++) {
Collection classAttributes = attributes.getAttributes(clazz[i]);
if (classAttributes != null) {
add(definition, classAttributes);
}
}
}
private void addInterfaceMethodAttributes(
ConfigAttributeDefinition definition, Method method) {
Class[] interfaces = method.getDeclaringClass().getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class clazz = interfaces[i];
try {
Method m = clazz.getDeclaredMethod(method.getName(),
method.getParameterTypes());
addMethodAttributes(definition, m);
} catch (Exception e) {
// this won't happen since we are getting a method from an interface that
// the declaring class implements
}
}
}
private void addMethodAttributes(ConfigAttributeDefinition definition,
Method method) {
// add the method level attributes
Collection methodAttributes = attributes.getAttributes(method);
if (methodAttributes != null) {
add(definition, methodAttributes);
}
}
}

View File

@ -0,0 +1,187 @@
/* 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.intercept.method;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Stores a {@link ConfigAttributeDefinition} for each method signature defined
* in a bean context.
*
* @author Ben Alex
* @version $Id$
*/
public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(MethodDefinitionMap.class);
//~ Instance fields ========================================================
/** Map from Method to ApplicationDefinition */
protected Map methodMap = new HashMap();
/** Map from Method to name pattern used for registration */
private Map nameMap = new HashMap();
//~ Methods ================================================================
public Iterator getConfigAttributeDefinitions() {
return methodMap.values().iterator();
}
public int getMethodMapSize() {
return this.methodMap.size();
}
/**
* Add configuration attributes for a secure method. Method names can end
* or start with <code>&#42</code> for matching multiple methods.
*
* @param method the method to be secured
* @param attr required authorities associated with the method
*/
public void addSecureMethod(Method method, ConfigAttributeDefinition attr) {
logger.info("Adding secure method [" + method + "] with attributes ["
+ attr + "]");
this.methodMap.put(method, attr);
}
/**
* Add configuration attributes for a secure method. Method names can end
* or start with <code>&#42</code> for matching multiple methods.
*
* @param name class and method name, separated by a dot
* @param attr required authorities associated with the method
*
* @throws IllegalArgumentException DOCUMENT ME!
*/
public void addSecureMethod(String name, ConfigAttributeDefinition attr) {
int lastDotIndex = name.lastIndexOf(".");
if (lastDotIndex == -1) {
throw new IllegalArgumentException("'" + name
+ "' is not a valid method name: format is FQN.methodName");
}
String className = name.substring(0, lastDotIndex);
String methodName = name.substring(lastDotIndex + 1);
try {
Class clazz = Class.forName(className, true,
Thread.currentThread().getContextClassLoader());
addSecureMethod(clazz, methodName, attr);
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException("Class '" + className
+ "' not found");
}
}
/**
* Add configuration attributes for a secure method. Method names can end
* or start with <code>&#42</code> for matching multiple methods.
*
* @param clazz target interface or class
* @param mappedName mapped method name
* @param attr required authorities associated with the method
*
* @throws IllegalArgumentException DOCUMENT ME!
*/
public void addSecureMethod(Class clazz, String mappedName,
ConfigAttributeDefinition attr) {
String name = clazz.getName() + '.' + mappedName;
if (logger.isDebugEnabled()) {
logger.debug("Adding secure method [" + name
+ "] with attributes [" + attr + "]");
}
Method[] methods = clazz.getDeclaredMethods();
List matchingMethods = new ArrayList();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(mappedName)
|| isMatch(methods[i].getName(), mappedName)) {
matchingMethods.add(methods[i]);
}
}
if (matchingMethods.isEmpty()) {
throw new IllegalArgumentException("Couldn't find method '"
+ mappedName + "' on " + clazz);
}
// register all matching methods
for (Iterator it = matchingMethods.iterator(); it.hasNext();) {
Method method = (Method) it.next();
String regMethodName = (String) this.nameMap.get(method);
if ((regMethodName == null)
|| (!regMethodName.equals(name)
&& (regMethodName.length() <= name.length()))) {
// no already registered method name, or more specific
// method name specification now -> (re-)register method
if (regMethodName != null) {
logger.debug("Replacing attributes for secure method ["
+ method + "]: current name [" + name
+ "] is more specific than [" + regMethodName + "]");
}
this.nameMap.put(method, name);
addSecureMethod(method, attr);
} else {
logger.debug("Keeping attributes for secure method [" + method
+ "]: current name [" + name
+ "] is not more specific than [" + regMethodName + "]");
}
}
}
protected ConfigAttributeDefinition lookupAttributes(MethodInvocation mi) {
return (ConfigAttributeDefinition) this.methodMap.get(mi.getMethod());
}
/**
* Return if the given method name matches the mapped name. The default
* implementation checks for "xxx" and "xxx" matches.
*
* @param methodName the method name of the class
* @param mappedName the name in the descriptor
*
* @return if the names match
*/
private boolean isMatch(String methodName, String mappedName) {
return (mappedName.endsWith("*")
&& methodName.startsWith(mappedName.substring(0, mappedName.length()
- 1)))
|| (mappedName.startsWith("*")
&& methodName.endsWith(mappedName.substring(1, mappedName.length())));
}
}

View File

@ -0,0 +1,29 @@
/* 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.intercept.method;
import net.sf.acegisecurity.intercept.ObjectDefinitionSource;
/**
* Marker interface for <code>ObjectDefinitionSource</code> implementations
* that are designed to perform lookups keyed on
* <code>MethodInvocation</code>s.
*
* @author Ben Alex
* @version $Id$
*/
public interface MethodDefinitionSource extends ObjectDefinitionSource {}

View File

@ -0,0 +1,82 @@
/* 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.intercept.method;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import net.sf.acegisecurity.ConfigAttributeEditor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.propertyeditors.PropertiesEditor;
import java.beans.PropertyEditorSupport;
import java.util.Iterator;
import java.util.Properties;
/**
* Property editor to assist with the setup of a {@link
* MethodDefinitionSource}.
*
* <p>
* The class creates and populates a {@link MethodDefinitionMap}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class MethodDefinitionSourceEditor extends PropertyEditorSupport {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(MethodDefinitionSourceEditor.class);
//~ Methods ================================================================
public void setAsText(String s) throws IllegalArgumentException {
MethodDefinitionMap source = new MethodDefinitionMap();
if ((s == null) || "".equals(s)) {
// Leave value in property editor null
} else {
// Use properties editor to tokenize the string
PropertiesEditor propertiesEditor = new PropertiesEditor();
propertiesEditor.setAsText(s);
Properties props = (Properties) propertiesEditor.getValue();
// Now we have properties, process each one individually
ConfigAttributeEditor configAttribEd = new ConfigAttributeEditor();
for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String value = props.getProperty(name);
// Convert value to series of security configuration attributes
configAttribEd.setAsText(value);
ConfigAttributeDefinition attr = (ConfigAttributeDefinition) configAttribEd
.getValue();
// Register name and attribute
source.addSecureMethod(name, attr);
}
}
setValue(source);
}
}

View File

@ -0,0 +1,92 @@
/* 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.intercept.method;
import net.sf.acegisecurity.intercept.AbstractSecurityInterceptor;
import net.sf.acegisecurity.intercept.ObjectDefinitionSource;
import net.sf.acegisecurity.intercept.SecurityInterceptorCallback;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* Provides security interception of method invocations.
*
* <p>
* The <code>ObjectDefinitionSource</code> required by this security
* interceptor is of type {@link MethodDefinitionSource}.
* </p>
*
* <P>
* Refer to {@link AbstractSecurityInterceptor} for details on the workflow.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class MethodSecurityInterceptor extends AbstractSecurityInterceptor
implements MethodInterceptor, SecurityInterceptorCallback {
//~ Instance fields ========================================================
private MethodDefinitionSource objectDefinitionSource;
//~ Methods ================================================================
public void setObjectDefinitionSource(MethodDefinitionSource newSource) {
this.objectDefinitionSource = newSource;
}
public MethodDefinitionSource getObjectDefinitionSource() {
return this.objectDefinitionSource;
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (!this.getAccessDecisionManager().supports(MethodInvocation.class)) {
throw new IllegalArgumentException(
"AccessDecisionManager does not support MethodInvocation");
}
if (!this.getRunAsManager().supports(MethodInvocation.class)) {
throw new IllegalArgumentException(
"RunAsManager does not support MethodInvocation");
}
}
/**
* This method should be used to enforce security on a
* <code>MethodInvocation</code>.
*
* @param mi The method being invoked which requires a security decision
*
* @return The returned value from the method invocation
*
* @throws Throwable if any error occurs
*/
public Object invoke(MethodInvocation mi) throws Throwable {
return super.interceptor(mi, this);
}
public ObjectDefinitionSource obtainObjectDefinitionSource() {
return this.objectDefinitionSource;
}
public Object proceedWithObject(Object object) throws Throwable {
return ((MethodInvocation) object).proceed();
}
}

View File

@ -0,0 +1,6 @@
<html>
<body>
Enforces security for <code>MethodInvocation</code>s, such as via
Spring AOP.
</body>
</html>

View File

@ -0,0 +1,31 @@
<html>
<body>
Actually enforces the security and ties the whole security system together.
<P>
A <i>secure object</i> is a term frequently used throughout the security
system. It does <b>not</b> refer to a business object that is being
secured, but instead refers to some infrastructure object that can have
security facilities provided for it by the Acegi Security System for
Spring. For example, one secure object would be
<code>MethodInvocation</code>, whilst another would be HTTP {@link
net.sf.acegisecurity.intercept.web.FilterInvocation}. Note these are
infrastructure objects and their design allows them to represent a large
variety of actual resources that might need to be secured, such as business
objects or HTTP request URLs.
</p>
<P>Each secure object typically has its
own <code>net.sf.acegisecurity.intercept</code> package.
Each package usually includes a concrete security interceptor (which
subclasses {@link net.sf.acegisecurity.intercept.AbstractSecurityInterceptor},
an appropriate {@link net.sf.acegisecurity.intercept.ObjectDefinitionSource}
for the type of resources the secure object represents, and a property editor
to populate the <code>ObjectDefinitionSource</code>.
<P>It is simple to create new secure object types, given the
<code>AbstractSecurityInterceptor</code> provides the majority of the logic
and other specialised packages provide the authentication, authorization,
run-as replacement management and <code>ContextHolder</code> population.
</body>
</html>

View File

@ -0,0 +1,71 @@
/* 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.intercept.web;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Abstract implementation of <Code>FilterInvocationDefinitionSource</code>.
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractFilterInvocationDefinitionSource
implements FilterInvocationDefinitionSource {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(AbstractFilterInvocationDefinitionSource.class);
//~ Methods ================================================================
public ConfigAttributeDefinition getAttributes(Object object)
throws IllegalArgumentException {
if ((object == null) || !this.supports(object.getClass())) {
throw new IllegalArgumentException(
"Object must be a FilterInvocation");
}
return this.lookupAttributes((FilterInvocation) object);
}
public boolean supports(Class clazz) {
if (FilterInvocation.class.isAssignableFrom(clazz)) {
return true;
} else {
return false;
}
}
/**
* Performs the actual lookup of the relevant
* <code>ConfigAttributeDefinition</code> for the specified
* <code>FilterInvocation</code>.
*
* <P>
* Provided so subclasses need only to provide one basic method to properly
* interface with the <code>FilterInvocationDefinitionSource</code>.
* </p>
*
* @return the <code>ConfigAttributeDefinition</code> that applies to the
* specified <code>FilterInvocation</code>
*/
protected abstract ConfigAttributeDefinition lookupAttributes(
FilterInvocation filterInvocation);
}

View File

@ -0,0 +1,109 @@
/* 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.intercept.web;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Holds objects associated with a HTTP filter.
*
* <P>
* Guarantees the request and response are instances of
* <code>HttpServletRequest</code> and <code>HttpServletResponse</code>, and
* that there are no <code>null</code> objects.
* </p>
*
* <P>
* Required so that security system classes can obtain access to the filter
* environment, as well as the request and response.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class FilterInvocation {
//~ Instance fields ========================================================
private FilterChain chain;
private ServletRequest request;
private ServletResponse response;
//~ Constructors ===========================================================
public FilterInvocation(ServletRequest request, ServletResponse response,
FilterChain chain) {
if ((request == null) || (response == null) || (chain == null)) {
throw new IllegalArgumentException(
"Cannot pass null values to constructor");
}
if (!(request instanceof HttpServletRequest)) {
throw new IllegalArgumentException(
"Can only process HttpServletRequest");
}
if (!(response instanceof HttpServletResponse)) {
throw new IllegalArgumentException(
"Can only process HttpServletResponse");
}
this.request = request;
this.response = response;
this.chain = chain;
}
protected FilterInvocation() {
throw new IllegalArgumentException("Cannot use default constructor");
}
//~ Methods ================================================================
public FilterChain getChain() {
return chain;
}
public HttpServletRequest getHttpRequest() {
return (HttpServletRequest) request;
}
public HttpServletResponse getHttpResponse() {
return (HttpServletResponse) response;
}
public ServletRequest getRequest() {
return request;
}
public String getRequestUrl() {
return getHttpRequest().getServletPath()
+ ((getHttpRequest().getQueryString() == null) ? ""
: ("?"
+ getHttpRequest().getQueryString()));
}
public ServletResponse getResponse() {
return response;
}
public String toString() {
return "FilterInvocation: URL: " + getRequestUrl();
}
}

View File

@ -0,0 +1,180 @@
/* 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.intercept.web;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.oro.text.regex.MalformedPatternException;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.PatternMatcher;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;
/**
* Maintains a <Code>List</code> of <code>ConfigAttributeDefinition</code>s
* associated with different HTTP request URL patterns.
*
* <P>
* Regular expressions are used to match a HTTP request URL against a
* <code>ConfigAttributeDefinition</code>.
* </p>
*
* <p>
* The order of registering the regular expressions using the {@link
* #addSecureUrl(String, ConfigAttributeDefinition)} is very important. The
* system will identify the <B>first</B> matching regular expression for a
* given HTTP URL. It will not proceed to evaluate later regular expressions
* if a match has already been found. Accordingly, the most specific regular
* expressions should be registered first, with the most general regular
* expressions registered last.
* </p>
*
* <P>
* If no registered regular expressions match the HTTP URL, <code>null</code>
* is returned.
* </p>
*/
public class FilterInvocationDefinitionMap
extends AbstractFilterInvocationDefinitionSource {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(FilterInvocationDefinitionMap.class);
//~ Instance fields ========================================================
private List requestMap = new Vector();
private boolean convertUrlToLowercaseBeforeComparison = false;
//~ Methods ================================================================
public Iterator getConfigAttributeDefinitions() {
Set set = new HashSet();
Iterator iter = requestMap.iterator();
while (iter.hasNext()) {
EntryHolder entryHolder = (EntryHolder) iter.next();
set.add(entryHolder.getConfigAttributeDefinition());
}
return set.iterator();
}
public void setConvertUrlToLowercaseBeforeComparison(
boolean convertUrlToLowercaseBeforeComparison) {
this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
}
public boolean isConvertUrlToLowercaseBeforeComparison() {
return convertUrlToLowercaseBeforeComparison;
}
public int getMapSize() {
return this.requestMap.size();
}
public void addSecureUrl(String perl5RegExp, ConfigAttributeDefinition attr) {
Pattern compiledPattern;
Perl5Compiler compiler = new Perl5Compiler();
try {
compiledPattern = compiler.compile(perl5RegExp,
Perl5Compiler.READ_ONLY_MASK);
} catch (MalformedPatternException mpe) {
throw new IllegalArgumentException("Malformed regular expression: "
+ perl5RegExp);
}
requestMap.add(new EntryHolder(compiledPattern, attr));
if (logger.isDebugEnabled()) {
logger.debug("Added regular expression: "
+ compiledPattern.getPattern().toString() + "; attributes: "
+ attr.toString());
}
}
protected ConfigAttributeDefinition lookupAttributes(
FilterInvocation filterInvocation) {
PatternMatcher matcher = new Perl5Matcher();
Iterator iter = requestMap.iterator();
String url = filterInvocation.getRequestUrl();
if (convertUrlToLowercaseBeforeComparison) {
url = url.toLowerCase();
if (logger.isDebugEnabled()) {
logger.debug("Converted URL to lowercase, from: '"
+ filterInvocation.getRequest() + "'; to: '" + url + "'");
}
}
while (iter.hasNext()) {
EntryHolder entryHolder = (EntryHolder) iter.next();
boolean matched = matcher.matches(url,
entryHolder.getCompiledPattern());
if (logger.isDebugEnabled()) {
logger.debug("Candidate is: '" + url + "'; pattern is "
+ entryHolder.getCompiledPattern().getPattern()
+ "; matched=" + matched);
}
if (matched) {
return entryHolder.getConfigAttributeDefinition();
}
}
return null;
}
//~ Inner Classes ==========================================================
protected class EntryHolder {
private ConfigAttributeDefinition configAttributeDefinition;
private Pattern compiledPattern;
public EntryHolder(Pattern compiledPattern,
ConfigAttributeDefinition attr) {
this.compiledPattern = compiledPattern;
this.configAttributeDefinition = attr;
}
protected EntryHolder() {
throw new IllegalArgumentException("Cannot use default constructor");
}
public Pattern getCompiledPattern() {
return compiledPattern;
}
public ConfigAttributeDefinition getConfigAttributeDefinition() {
return configAttributeDefinition;
}
}
}

View File

@ -0,0 +1,28 @@
/* 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.intercept.web;
import net.sf.acegisecurity.intercept.ObjectDefinitionSource;
/**
* Marker interface for <code>ObjectDefinitionSource</code> implementations
* that are designed to perform lookups keyed on {@link FilterInvocation}s.
*
* @author Ben Alex
* @version $Id$
*/
public interface FilterInvocationDefinitionSource extends ObjectDefinitionSource {}

View File

@ -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.intercept.web;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import net.sf.acegisecurity.ConfigAttributeEditor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
import java.beans.PropertyEditorSupport;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
/**
* Property editor to assist with the setup of {@link
* FilterInvocationDefinitionSource}.
*
* <p>
* The class creates and populates a {@link FilterInvocationDefinitionMap}.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class FilterInvocationDefinitionSourceEditor
extends PropertyEditorSupport {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(FilterInvocationDefinitionSourceEditor.class);
//~ Methods ================================================================
public void setAsText(String s) throws IllegalArgumentException {
FilterInvocationDefinitionMap source = new FilterInvocationDefinitionMap();
if ((s == null) || "".equals(s)) {
// Leave value in property editor null
} else {
BufferedReader br = new BufferedReader(new StringReader(s));
int counter = 0;
String line;
while (true) {
counter++;
try {
line = br.readLine();
} catch (IOException ioe) {
throw new IllegalArgumentException(ioe.getMessage());
}
if (line == null) {
break;
}
line = line.trim();
if (logger.isDebugEnabled()) {
logger.debug("Line " + counter + ": " + line);
}
if (line.startsWith("//")) {
continue;
}
if (line.equals("CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON")) {
if (logger.isDebugEnabled()) {
logger.debug("Line " + counter
+ ": Instructing mapper to convert URLs to lowercase before comparison");
}
source.setConvertUrlToLowercaseBeforeComparison(true);
continue;
}
if (line.lastIndexOf('=') == -1) {
continue;
}
// Tokenize the line into its name/value tokens
String[] nameValue = StringUtils.delimitedListToStringArray(line,
"=");
String name = nameValue[0];
String value = nameValue[1];
// Convert value to series of security configuration attributes
ConfigAttributeEditor configAttribEd = new ConfigAttributeEditor();
configAttribEd.setAsText(value);
ConfigAttributeDefinition attr = (ConfigAttributeDefinition) configAttribEd
.getValue();
// Register the regular expression and its attribute
source.addSecureUrl(name, attr);
}
}
setValue(source);
}
}

View File

@ -0,0 +1,91 @@
/* 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.intercept.web;
import net.sf.acegisecurity.intercept.AbstractSecurityInterceptor;
import net.sf.acegisecurity.intercept.ObjectDefinitionSource;
import net.sf.acegisecurity.intercept.SecurityInterceptorCallback;
/**
* Performs security handling of HTTP resources via a filter implementation.
*
* <P>
* End users should <B>only</B> use this class to configure their HTTP security
* configuration in an application context. They should <B>not</B> attempt to
* invoke the <code>FilterSecurityInterceptor</code> except as a standard bean
* registration in an application context. At runtime, this class will provide
* services to web applications via the {@link SecurityEnforcementFilter}.
* </p>
*
* <p>
* The <code>ObjectDefinitionSource</code> required by this security
* interceptor is of type {@link FilterInvocationDefinitionSource}.
* </p>
*
* <P>
* Refer to {@link AbstractSecurityInterceptor} for details on the workflow.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor
implements SecurityInterceptorCallback {
//~ Instance fields ========================================================
private FilterInvocationDefinitionSource objectDefinitionSource;
//~ Methods ================================================================
public void setObjectDefinitionSource(
FilterInvocationDefinitionSource newSource) {
this.objectDefinitionSource = newSource;
}
public FilterInvocationDefinitionSource getObjectDefinitionSource() {
return this.objectDefinitionSource;
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (!this.getAccessDecisionManager().supports(FilterInvocation.class)) {
throw new IllegalArgumentException(
"AccessDecisionManager does not support FilterInvocation");
}
if (!this.getRunAsManager().supports(FilterInvocation.class)) {
throw new IllegalArgumentException(
"RunAsManager does not support FilterInvocation");
}
}
public void invoke(FilterInvocation fi) throws Throwable {
super.interceptor(fi, this);
}
public ObjectDefinitionSource obtainObjectDefinitionSource() {
return this.objectDefinitionSource;
}
public Object proceedWithObject(Object object) throws Throwable {
FilterInvocation fi = (FilterInvocation) object;
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return null;
}
}

View File

@ -0,0 +1,178 @@
/* 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.intercept.web;
import net.sf.acegisecurity.AccessDeniedException;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
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;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Wraps requests to the {@link FilterSecurityInterceptor}.
*
* <P>
* This filter is necessary because it provides an application context
* environment for the <code>FilterSecurityInterceptor</code> instance.
* </p>
*
* <P>
* If a {@link AuthenticationException} is detected, the filter will redirect
* to the <code>loginFormUrl</code>. This allows common handling of
* authentication failures originating from any subclass of {@link
* net.sf.acegisecurity.intercept.AbstractSecurityInterceptor}.
* </p>
*
* <p>
* If an {@link AccessDeniedException} is detected, the filter will response
* with a <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error). Again,
* this allows common access denied handling irrespective of the originating
* security interceptor.
* </p>
*
* <P>
* To use this filter, it is necessary to specify the following filter
* initialization parameters:
*
* <ul>
* <li>
* <code>appContextLocation</code> indicates the path to an application context
* that contains the <code>FilterSecurityInterceptor</code>.
* </li>
* <li>
* <code>loginFormUrl</code> indicates the URL that should be used for
* redirection if an <code>AuthenticationException</code> is detected.
* </li>
* </ul>
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class SecurityEnforcementFilter implements Filter {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(SecurityEnforcementFilter.class);
//~ Instance fields ========================================================
protected ClassPathXmlApplicationContext ctx;
protected FilterSecurityInterceptor securityInterceptor;
/**
* The URL that should be used for redirection if an
* <code>AuthenticationException</code> is detected.
*/
protected String loginFormUrl;
//~ Methods ================================================================
public void destroy() {
ctx.close();
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("HttpServletRequest required");
}
if (!(response instanceof HttpServletResponse)) {
throw new ServletException("HttpServletResponse required");
}
FilterInvocation fi = new FilterInvocation(request, response, chain);
try {
securityInterceptor.invoke(fi);
if (logger.isDebugEnabled()) {
logger.debug("Chain processed normally");
}
} catch (AuthenticationException authentication) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (logger.isDebugEnabled()) {
logger.debug(
"Authentication failed - adding target URL to Session: "
+ fi.getRequestUrl());
}
((HttpServletRequest) request).getSession().setAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY,
fi.getRequestUrl());
((HttpServletResponse) response).sendRedirect(((HttpServletRequest) request)
.getContextPath() + loginFormUrl);
} catch (AccessDeniedException accessDenied) {
if (logger.isDebugEnabled()) {
logger.debug(
"Access is denied - sending back forbidden response");
}
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN); // 403
} catch (Throwable otherException) {
throw new ServletException(otherException);
}
}
public void init(FilterConfig filterConfig) throws ServletException {
String appContextLocation = filterConfig.getInitParameter(
"appContextLocation");
if ((appContextLocation == null) || "".equals(appContextLocation)) {
throw new ServletException("appContextLocation must be specified");
}
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");
}
ctx = new ClassPathXmlApplicationContext(appContextLocation);
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);
}
}

View File

@ -0,0 +1,5 @@
<html>
<body>
Enforces security for HTTP requests, typically by the URL requested.
</body>
</html>

View File

@ -0,0 +1,161 @@
/* 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.ui;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.context.Context;
import net.sf.acegisecurity.context.ContextHolder;
import net.sf.acegisecurity.context.SecureContext;
import net.sf.acegisecurity.context.SecureContextImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* Automatically populates a {@link net.sf.acegisecurity.context.SecureContext}
* from a subclass-provided <code>Authentication</code> object.
*
* <p>
* The container hosting the Acegi Security System for Spring secured
* application is expected to expose an {@link Authentication} object in a
* well-known location. The <code>Authentication</code> object will have been
* created by the Acegi Security System for Spring and placed into the
* well-known location via approaches such as container adapters or container
* sessions.
* </p>
*
* <P>
* Once the <code>Authentication</code> object has been extracted from the
* well-known location, the <code>AbstractIntegrationFilter</code> handles
* putting it into the {@link ContextHolder}. It then removes it once the
* filter chain has completed.
* </p>
*
* <p>
* This filter will not abort if an <code>Authentication</code> object cannot
* be obtained from the well-known location. It will simply continue the
* filter chain as normal.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractIntegrationFilter implements Filter {
//~ Static fields/initializers =============================================
protected static final Log logger = LogFactory.getLog(AbstractIntegrationFilter.class);
//~ Methods ================================================================
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Populate authentication information
Object extracted = this.extractFromContainer(request);
if (extracted instanceof Authentication) {
if (logger.isDebugEnabled()) {
logger.debug(
"Authentication added to ContextHolder from container");
}
Authentication auth = (Authentication) extracted;
// Get or create existing SecureContext
SecureContext secureContext = null;
if ((ContextHolder.getContext() == null)
|| !(ContextHolder.getContext() instanceof SecureContext)) {
secureContext = new SecureContextImpl();
} else {
secureContext = (SecureContext) ContextHolder.getContext();
}
// Add Authentication to SecureContext, and save
secureContext.setAuthentication(auth);
ContextHolder.setContext((Context) secureContext);
} else {
if (logger.isDebugEnabled()) {
logger.debug(
"Authentication not added to ContextHolder (could not extract an authentication object from the container which is an instance of Authentication)");
}
}
// Proceed with chain
chain.doFilter(request, response);
// Remove authentication information
if ((ContextHolder.getContext() != null)
&& ContextHolder.getContext() instanceof SecureContext) {
if (logger.isDebugEnabled()) {
logger.debug("Removing Authentication from ContextHolder");
}
// Get context holder and remove authentication information
SecureContext secureContext = (SecureContext) ContextHolder
.getContext();
secureContext.setAuthentication(null);
ContextHolder.setContext((Context) secureContext);
} else {
if (logger.isDebugEnabled()) {
logger.debug(
"ContextHolder does not contain any authentication information");
}
}
}
/**
* Subclasses must override this method to provide the <code>Object</code>
* that contains the <code>Authentication</code> interface.
*
* <p>
* For convenience we have allowed any <code>Object</code> to be returned
* by subclasses, as the abstract class will ensure class casting safety
* and ignore objects that do not implement <code>Authentication</code>.
* </p>
*
* <p>
* If no <code>Authentication</code> object is available, subclasses should
* return <code>null</code>.
* </p>
*
* <p>
* If the subclass can locate multiple authentication objects, they should
* return the object that was created by the Acegi Security System for
* Spring (ie the object that implements <code>Authentication</code>).
* </p>
*
* @param request the request, which may be of use in extracting the
* authentication object
*
* @return <code>null</code> or an object that implements
* <code>Authentication</code>
*/
public abstract Object extractFromContainer(ServletRequest request);
public void init(FilterConfig filterConfig) throws ServletException {}
}

View File

@ -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.ui;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.adapters.HttpRequestIntegrationFilter;
import net.sf.acegisecurity.adapters.jboss.JbossIntegrationFilter;
import net.sf.acegisecurity.ui.webapp.HttpSessionIntegrationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
/**
* Detects the container and delegates to the appropriate {@link
* AbstractIntegrationFilter}.
*
* <p>
* This eases the creation of portable, secure applications, as the
* <code>web.xml</code> will not need to refer to a specific integration
* filter.
* </p>
*
* <P>
* The filter automatically delegates to {@link HttpRequestIntegrationFilter}
* if any <code>Authentication</code> object is detected in the
* <code>ServletRequest</code>. Failing this, it will delegate to {@link
* HttpSessionIntegrationFilter} if the session object contains an
* <code>Authentication</code> object. Finally, this filter will delegate to
* {@link JbossIntegrationFilter} if the <code>ServletRequest</code> contains
* an instance of JBoss' <code>SimplePrincipal</code>.
* </p>
*
* <P>
* If no location can be found containing the <code>Authentication</code>
* object, it will return <code>null</code>.
* </p>
*
* @author Ben Alex
* @version $Id$
*
* @see AbstractIntegrationFilter
*/
public class AutoIntegrationFilter extends AbstractIntegrationFilter {
//~ Methods ================================================================
public Object extractFromContainer(ServletRequest request) {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (httpRequest.getUserPrincipal() instanceof Authentication) {
return getHttpServletRequest().extractFromContainer(request);
}
if (getHttpSessionIntegrationFilter().extractFromContainer(request) != null) {
return getHttpSessionIntegrationFilter().extractFromContainer(request);
}
try {
Class simplePrincipalClass = Class.forName(
"org.jboss.security.SimplePrincipal");
if (null != httpRequest.getUserPrincipal()) {
if (simplePrincipalClass.isAssignableFrom(
httpRequest.getUserPrincipal().getClass())) {
return getJbossIntegrationFilter().extractFromContainer(request);
}
}
} catch (ClassNotFoundException e) {
// Can't be JBoss principal
// Expected, and normal - fall through
}
}
return null;
}
/**
* Allows test case to override the source of
* <code>HttpRequestIntegrationFilter</code>.
*
* @return the <code>HttpRequestIntegrationFilter</code> to use
*/
protected HttpRequestIntegrationFilter getHttpServletRequest() {
return new HttpRequestIntegrationFilter();
}
/**
* Allows test case to override the source of
* <code>HttpSessionIntegrationFilter</code>.
*
* @return the <code>HttpRequestIntegrationFilter</code> to use
*/
protected HttpSessionIntegrationFilter getHttpSessionIntegrationFilter() {
return new HttpSessionIntegrationFilter();
}
/**
* Allows test case to override the source of
* <code>JbossIntegrationFilter</code>.
*
* @return the <code>JbossIntegrationFilter</code> to use
*/
protected JbossIntegrationFilter getJbossIntegrationFilter() {
return new JbossIntegrationFilter();
}
}

View File

@ -0,0 +1,6 @@
<html>
<body>
Interfaces the system with various end-user authentication approaches, such
as container adapters and web applications.
</body>
</html>

View File

@ -0,0 +1,263 @@
/* 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.ui.webapp;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.AuthenticationManager;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
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;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Processes an authentication form, putting the result into the
* <code>HttpSession</code>.
*
* <p>
* This filter is responsible for processing authentication requests. A user
* will typically authenticate once using a login form, and this filter
* processes that form. If authentication is successful, the resulting {@link
* Authentication} object will be placed into the <code>HttpSession</code>
* with the attribute defined by {@link
* HttpSessionIntegrationFilter#ACEGI_SECURITY_AUTHENTICATION_KEY}.
* </p>
*
* <P>
* Login forms must present two parameters to this filter: a username and
* password. The filter will process the login against the authentication
* environment that was configured from a Spring application context defined
* in the filter initialization.
* </p>
*
* <P>
* If authentication fails, the <code>AuthenticationException</code> will be
* placed into the <code>HttpSession</code> with the attribute defined by
* {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}.
* </p>
*
* <P>
* To use this filter, it is necessary to specify the following filter
* initialization parameters:
*
* <ul>
* <li>
* <code>appContextLocation</code> indicates the path to an application context
* that contains an {@link AuthenticationManager} that should be used to
* process each authentication request.
* </li>
* <li>
* <code>defaultTargetUrl</code> indicates the URL that should be used for
* redirection if the <code>HttpSession</code> attribute named {@link
* #ACEGI_SECURITY_TARGET_URL_KEY} does not indicate the target URL once
* authentication is completed successfully. eg: <code>/</code>.
* </li>
* <li>
* <code>authenticationFailureUrl</code> indicates the URL that should be used
* for redirection if the authentication request fails. eg:
* <code>/login.jsp?login_error=1</code>.
* </li>
* <li>
* <code>filterProcessesUrl</code> indicates the URL that this filter will
* respond to. This parameter is optional, and defaults to
* <code>/j_acegi_security_check</code>.
* </li>
* </ul>
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class AuthenticationProcessingFilter implements Filter {
//~ Static fields/initializers =============================================
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";
public static final String ACEGI_SECURITY_LAST_EXCEPTION_KEY = "ACEGI_SECURITY_LAST_EXCEPTION";
private static final Log logger = LogFactory.getLog(AuthenticationProcessingFilter.class);
//~ Instance fields ========================================================
private AuthenticationManager authenticationManager;
private ClassPathXmlApplicationContext ctx;
/** Where to redirect the browser to if authentication fails */
private String authenticationFailureUrl;
/**
* Where to redirect the browser to if authentication is successful but
* ACEGI_SECURITY_TARGET_URL_KEY is <code>null</code>
*/
private String defaultTargetUrl;
/**
* The URL destination that this filter intercepts and processes (usually
* <code>/j_acegi_security_check</code>)
*/
private String filterProcessesUrl;
//~ Methods ================================================================
public void destroy() {
ctx.close();
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("Can only process HttpServletRequest");
}
if (!(response instanceof HttpServletResponse)) {
throw new ServletException("Can only process HttpServletResponse");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (filterProcessesUrl.equals(httpRequest.getServletPath())) {
if (logger.isDebugEnabled()) {
logger.debug("Request is to process Acegi login form");
}
String username = httpRequest.getParameter(ACEGI_SECURITY_FORM_USERNAME_KEY);
String password = httpRequest.getParameter(ACEGI_SECURITY_FORM_PASSWORD_KEY);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,
password);
Authentication authResult;
try {
authResult = authenticationManager.authenticate(authRequest);
} catch (AuthenticationException failed) {
// Authentication failed
if (logger.isDebugEnabled()) {
logger.debug("Authentication request for user: " + username
+ " failed: " + failed.toString());
}
httpRequest.getSession().setAttribute(ACEGI_SECURITY_LAST_EXCEPTION_KEY,
failed);
httpRequest.getSession().setAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY,
null);
httpResponse.sendRedirect(httpRequest.getContextPath()
+ authenticationFailureUrl);
return;
}
// Authentication success
if (logger.isDebugEnabled()) {
logger.debug("Authentication success: " + authResult.toString());
}
httpRequest.getSession().setAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY,
authResult);
String targetUrl = (String) httpRequest.getSession().getAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY);
httpRequest.getSession().setAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY,
null);
if (targetUrl == null) {
targetUrl = defaultTargetUrl;
}
if (logger.isDebugEnabled()) {
logger.debug(
"Redirecting to target URL from HTTP Session (or default): "
+ targetUrl);
}
httpResponse.sendRedirect(httpRequest.getContextPath() + targetUrl);
return;
}
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
String appContextLocation = filterConfig.getInitParameter(
"appContextLocation");
if ((appContextLocation == null) || "".equals(appContextLocation)) {
throw new ServletException("appContextLocation must be specified");
}
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";
}
ctx = new ClassPathXmlApplicationContext(appContextLocation);
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);
}
}

View File

@ -0,0 +1,82 @@
/* 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.ui.webapp;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.ui.AbstractIntegrationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* Populates a {@link net.sf.acegisecurity.context.SecureContext} from the
* <code>HttpSession</code>.
*
* <P>
* The filter will inspect the <code>HttpSession</code> for an attribute with
* the name indicated by {@link #ACEGI_SECURITY_AUTHENTICATION_KEY}. If that
* attribute contains an instance of {@link Authentication}, it will be placed
* into the <code>ContextHolder</code>.
* </p>
*
* <P>
* This filter is normally used in conjunction with {@link
* AuthenticationProcessingFilter}, which populates the
* <code>HttpSession</code> with an <code>Authentication</code> object based
* on a form login. Alternatively, users may elect to use their own approach
* for populating the <code>HttpSession</code>.
* </p>
*
* <p>
* As with other <code>AbstractIntegrationFilter</code>s, this filter will
* ensure the <code>ContextHolder</code> is populated with the
* <code>Authentication</code> object for the duration of the HTTP request,
* and is unbound from the <code>ContextHolder</code> at the completion of the
* request.
* </p>
*
* <p>
* See {@link AbstractIntegrationFilter} for further information.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class HttpSessionIntegrationFilter extends AbstractIntegrationFilter {
//~ Static fields/initializers =============================================
public static final String ACEGI_SECURITY_AUTHENTICATION_KEY = "ACEGI_SECURITY_AUTHENTICATION";
//~ Methods ================================================================
public Object extractFromContainer(ServletRequest request) {
if (request instanceof HttpServletRequest) {
HttpSession httpSession = ((HttpServletRequest) request).getSession();
if (httpSession != null) {
Object authObject = httpSession.getAttribute(ACEGI_SECURITY_AUTHENTICATION_KEY);
if (authObject instanceof Authentication) {
return authObject;
}
}
}
return null;
}
}

View File

@ -0,0 +1,5 @@
<html>
<body>
Authenticates users via a standard web form and <code>HttpSession</code>.
</body>
</html>