Przeglądaj źródła

SEC-1033: Completed working version of web expression support.
SEC-999: Added getExpressionParser() method to the security handler interface to allow both web and method expression security to obtain a suitable parser from the configuration for parsing their expression attributes.

Luke Taylor 17 lat temu
rodzic
commit
6b4045667a

+ 1 - 9
core/src/main/java/org/springframework/security/config/ConfigUtils.java

@@ -31,14 +31,6 @@ import org.w3c.dom.Element;
  */
 abstract class ConfigUtils {
 
-    @SuppressWarnings("unchecked")
-    static void registerDefaultWebAccessManagerIfNecessary(ParserContext parserContext) {
-        if (!parserContext.getRegistry().containsBeanDefinition(BeanIds.WEB_ACCESS_MANAGER)) {
-            parserContext.getRegistry().registerBeanDefinition(BeanIds.WEB_ACCESS_MANAGER,
-                    createAccessManagerBean(RoleVoter.class, AuthenticatedVoter.class));
-        }
-    }
-
     @SuppressWarnings("unchecked")
     static void registerDefaultMethodAccessManagerIfNecessary(ParserContext parserContext) {
         if (!parserContext.getRegistry().containsBeanDefinition(BeanIds.METHOD_ACCESS_MANAGER)) {
@@ -48,7 +40,7 @@ abstract class ConfigUtils {
     }
 
     @SuppressWarnings("unchecked")
-    private static BeanDefinition createAccessManagerBean(Class<? extends AccessDecisionVoter>... voters) {
+    static BeanDefinition createAccessManagerBean(Class<? extends AccessDecisionVoter>... voters) {
         ManagedList defaultVoters = new ManagedList(voters.length);
 
         for(Class<? extends AccessDecisionVoter> voter : voters) {

+ 38 - 31
core/src/main/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParser.java

@@ -37,17 +37,18 @@ import org.w3c.dom.Element;
  * Processes the top-level "global-method-security" element.
  *
  * @author Ben Alex
+ * @author Luke Taylor
  * @version $Id$
+ * @since 2.0
  */
 class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
 
     private final Log logger = LogFactory.getLog(getClass());
 
-    static final String SECURED_DEPENDENCY_CLASS = "org.springframework.security.annotation.Secured";
-    static final String SECURED_METHOD_DEFINITION_SOURCE_CLASS = "org.springframework.security.annotation.SecuredMethodDefinitionSource";
-    static final String EXPRESSION_METHOD_DEFINITION_SOURCE_CLASS = "org.springframework.security.expression.method.ExpressionAnnotationMethodDefinitionSource";
-    static final String JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS = "org.springframework.security.annotation.Jsr250MethodDefinitionSource";
-    static final String JSR_250_VOTER_CLASS = "org.springframework.security.annotation.Jsr250Voter";
+    private static final String SECURED_METHOD_DEFINITION_SOURCE_CLASS = "org.springframework.security.annotation.SecuredMethodDefinitionSource";
+    private static final String EXPRESSION_METHOD_DEFINITION_SOURCE_CLASS = "org.springframework.security.expression.method.ExpressionAnnotationMethodDefinitionSource";
+    private static final String JSR_250_SECURITY_METHOD_DEFINITION_SOURCE_CLASS = "org.springframework.security.annotation.Jsr250MethodDefinitionSource";
+    private static final String JSR_250_VOTER_CLASS = "org.springframework.security.annotation.Jsr250Voter";
 
     /*
      * Internal Bean IDs which are only used within this class
@@ -56,7 +57,7 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
     static final String INTERCEPTOR_POST_PROCESSOR_ID = "_globalMethodSecurityInterceptorPostProcessor";
     static final String ACCESS_MANAGER_ID = "_globalMethodSecurityAccessManager";
     private static final String DELEGATING_METHOD_DEFINITION_SOURCE_ID = "_delegatingMethodDefinitionSource";
-    private static final String EXPRESSION_HANDLER_ID = "_expressionHandler";
+    private static final String EXPRESSION_HANDLER_ID = "_methodExpressionHandler";
 
     private static final String ATT_ACCESS = "access";
     private static final String ATT_EXPRESSION = "expression";
@@ -74,9 +75,33 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
         boolean jsr250Enabled = "enabled".equals(element.getAttribute(ATT_USE_JSR250));
         boolean useSecured = "enabled".equals(element.getAttribute(ATT_USE_SECURED));
         boolean expressionsEnabled = "enabled".equals(element.getAttribute(ATT_USE_EXPRESSIONS));
+        BeanDefinition expressionVoter = null;
 
         if (expressionsEnabled) {
-            delegates.add(BeanDefinitionBuilder.rootBeanDefinition(EXPRESSION_METHOD_DEFINITION_SOURCE_CLASS).getBeanDefinition());
+            Element expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER);
+            String expressionHandlerRef = expressionHandlerElt == null ? null : expressionHandlerElt.getAttribute("ref");
+
+            if (StringUtils.hasText(expressionHandlerRef)) {
+                logger.info("Using bean '" + expressionHandlerRef + "' as method SecurityExpressionHandler implementation");
+            } else {
+                parserContext.getRegistry().registerBeanDefinition(EXPRESSION_HANDLER_ID, new RootBeanDefinition(DefaultSecurityExpressionHandler.class));
+                logger.warn("Expressions were enabled for method security but no SecurityExpressionHandler was configured. " +
+                        "All hasPermision() expressions will evaluate to false.");
+                expressionHandlerRef = EXPRESSION_HANDLER_ID;
+            }
+            BeanDefinitionBuilder expressionVoterBldr = BeanDefinitionBuilder.rootBeanDefinition(MethodExpressionVoter.class);
+            BeanDefinitionBuilder afterInvocationProvider = BeanDefinitionBuilder.rootBeanDefinition(MethodExpressionAfterInvocationProvider.class);
+            expressionVoterBldr.addPropertyReference("expressionHandler", expressionHandlerRef);
+            expressionVoter = expressionVoterBldr.getBeanDefinition();
+            // After-invocation provider to handle post-invocation filtering and authorization expression annotations.
+            afterInvocationProvider.addPropertyReference("expressionHandler", expressionHandlerRef);
+            ConfigUtils.getRegisteredAfterInvocationProviders(parserContext).add(afterInvocationProvider.getBeanDefinition());
+            // Add the expression method definition source, which will obtain its parser from the registered expression
+            // handler
+            BeanDefinitionBuilder mds = BeanDefinitionBuilder.rootBeanDefinition(EXPRESSION_METHOD_DEFINITION_SOURCE_CLASS);
+            mds.addConstructorArgReference(expressionHandlerRef);
+
+            delegates.add(mds.getBeanDefinition());
         }
 
         if (useSecured) {
@@ -103,7 +128,7 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
         String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
 
         if (!StringUtils.hasText(accessManagerId)) {
-            registerAccessManager(element, parserContext, jsr250Enabled, expressionsEnabled);
+            registerAccessManager(parserContext, jsr250Enabled, expressionVoter);
             accessManagerId = ACCESS_MANAGER_ID;
         }
 
@@ -118,35 +143,17 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
 
     /**
      * Register the default AccessDecisionManager. Adds the special JSR 250 voter jsr-250 is enabled and an
-     * expression voter if expression-based access control is enabled. If expressions are in use, a after-invocation
-     * provider will also be registered to handle post-invocation filtering and authorization expression annotations.
+     * expression voter if expression-based access control is enabled.
      */
     @SuppressWarnings("unchecked")
-    private void registerAccessManager(Element element, ParserContext pc, boolean jsr250Enabled, boolean expressionsEnabled) {
-        Element expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER);
+    private void registerAccessManager(ParserContext pc, boolean jsr250Enabled, BeanDefinition expressionVoter) {
+
         BeanDefinitionBuilder accessMgrBuilder = BeanDefinitionBuilder.rootBeanDefinition(AffirmativeBased.class);
         ManagedList voters = new ManagedList(4);
 
-        if (expressionsEnabled) {
-            BeanDefinitionBuilder expressionVoter = BeanDefinitionBuilder.rootBeanDefinition(MethodExpressionVoter.class);
-            BeanDefinitionBuilder afterInvocationProvider = BeanDefinitionBuilder.rootBeanDefinition(MethodExpressionAfterInvocationProvider.class);
-            String expressionHandlerRef = expressionHandlerElt == null ? null : expressionHandlerElt.getAttribute("ref");
-
-            if (StringUtils.hasText(expressionHandlerRef)) {
-                logger.info("Using bean '" + expressionHandlerRef + "' as SecurityExpressionHandler implementation");
-            } else {
-                pc.getRegistry().registerBeanDefinition(EXPRESSION_HANDLER_ID, new RootBeanDefinition(DefaultSecurityExpressionHandler.class));
-                logger.warn("Expressions were enabled but no SecurityExpressionHandler was configured. " +
-                        "All hasPermision() expressions will evaluate to false.");
-                expressionHandlerRef = EXPRESSION_HANDLER_ID;
-            }
-
-            expressionVoter.addPropertyReference("expressionHandler", expressionHandlerRef);
-            afterInvocationProvider.addPropertyReference("expressionHandler", expressionHandlerRef);
-            ConfigUtils.getRegisteredAfterInvocationProviders(pc).add(afterInvocationProvider.getBeanDefinition());
-            voters.add(expressionVoter.getBeanDefinition());
+        if (expressionVoter != null) {
+            voters.add(expressionVoter);
         }
-
         voters.add(new RootBeanDefinition(RoleVoter.class));
         voters.add(new RootBeanDefinition(AuthenticatedVoter.class));
 

+ 55 - 18
core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java

@@ -1,5 +1,6 @@
 package org.springframework.security.config;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -20,6 +21,7 @@ import org.springframework.security.ConfigAttributeEditor;
 import org.springframework.security.SecurityConfig;
 import org.springframework.security.context.HttpSessionSecurityContextRepository;
 import org.springframework.security.context.SecurityContextPersistenceFilter;
+import org.springframework.security.expression.web.WebExpressionVoter;
 import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;
 import org.springframework.security.intercept.web.FilterSecurityInterceptor;
 import org.springframework.security.intercept.web.RequestKey;
@@ -37,6 +39,9 @@ import org.springframework.security.util.AntUrlPathMatcher;
 import org.springframework.security.util.FilterChainProxy;
 import org.springframework.security.util.RegexUrlPathMatcher;
 import org.springframework.security.util.UrlMatcher;
+import org.springframework.security.vote.AccessDecisionVoter;
+import org.springframework.security.vote.AuthenticatedVoter;
+import org.springframework.security.vote.RoleVoter;
 import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter;
 import org.springframework.util.StringUtils;
 import org.springframework.util.xml.DomUtils;
@@ -101,6 +106,10 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
 
     private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
 
+    private static final String EXPRESSION_FIDS_CLASS = "org.springframework.security.expression.web.ExpressionBasedFilterInvocationDefinitionSource";
+    private static final String EXPRESSION_HANDLER_CLASS = "org.springframework.security.expression.support.DefaultSecurityExpressionHandler";
+    private static final String EXPRESSION_HANDLER_ID = "_webExpressionHandler";
+
     @SuppressWarnings("unchecked")
     public BeanDefinition parse(Element element, ParserContext parserContext) {
         ConfigUtils.registerProviderManagerIfNecessary(parserContext);
@@ -125,14 +134,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
 
         registerServletApiFilter(element, parserContext);
 
-        // Set up the access manager reference for http
-        String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
-
-        if (!StringUtils.hasText(accessManagerId)) {
-            ConfigUtils.registerDefaultWebAccessManagerIfNecessary(parserContext);
-            accessManagerId = BeanIds.WEB_ACCESS_MANAGER;
-        }
-
         // Register the portMapper. A default will always be created, even if no element exists.
         BeanDefinition portMapper = new PortMappingsBeanDefinitionParser().parse(
                 DomUtils.getChildElementByTagName(element, Elements.PORT_MAPPINGS), parserContext);
@@ -140,7 +141,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
 
         registerExceptionTranslationFilter(element, parserContext, allowSessionCreation);
 
-
         if (channelRequestMap.size() > 0) {
             // At least one channel requirement has been specified
             registerChannelProcessingBeans(parserContext, matcher, channelRequestMap);
@@ -148,8 +148,47 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
 
         boolean useExpressions = "true".equals(element.getAttribute(ATT_USE_EXPRESSIONS));
 
-        registerFilterSecurityInterceptor(element, parserContext, matcher, accessManagerId,
-                parseInterceptUrlsForFilterInvocationRequestMap(interceptUrlElts, convertPathsToLowerCase, useExpressions, parserContext));
+        LinkedHashMap<RequestKey, List<ConfigAttribute>> requestToAttributesMap =
+            parseInterceptUrlsForFilterInvocationRequestMap(interceptUrlElts, convertPathsToLowerCase, useExpressions, parserContext);
+
+        BeanDefinitionBuilder fidsBuilder;
+        Class<? extends AccessDecisionVoter>[] voters;
+
+        if (useExpressions) {
+            Element expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER);
+            String expressionHandlerRef = expressionHandlerElt == null ? null : expressionHandlerElt.getAttribute("ref");
+
+            if (StringUtils.hasText(expressionHandlerRef)) {
+                logger.info("Using bean '" + expressionHandlerRef + "' as web SecurityExpressionHandler implementation");
+            } else {
+                parserContext.getRegistry().registerBeanDefinition(EXPRESSION_HANDLER_ID,
+                        BeanDefinitionBuilder.rootBeanDefinition(EXPRESSION_HANDLER_CLASS).getBeanDefinition());
+                expressionHandlerRef = EXPRESSION_HANDLER_ID;
+            }
+
+            fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(EXPRESSION_FIDS_CLASS);
+            fidsBuilder.addConstructorArgValue(matcher);
+            fidsBuilder.addConstructorArgValue(requestToAttributesMap);
+            fidsBuilder.addConstructorArgReference(expressionHandlerRef);
+            voters = new Class[] {WebExpressionVoter.class};
+        } else {
+            fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefaultFilterInvocationDefinitionSource.class);
+            fidsBuilder.addConstructorArgValue(matcher);
+            fidsBuilder.addConstructorArgValue(requestToAttributesMap);
+            voters = new Class[] {RoleVoter.class, AuthenticatedVoter.class};
+        }
+        fidsBuilder.addPropertyValue("stripQueryStringFromUrls", matcher instanceof AntUrlPathMatcher);
+
+        // Set up the access manager reference for http
+        String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
+
+        if (!StringUtils.hasText(accessManagerId)) {
+            parserContext.getRegistry().registerBeanDefinition(BeanIds.WEB_ACCESS_MANAGER,
+                        ConfigUtils.createAccessManagerBean(voters));
+            accessManagerId = BeanIds.WEB_ACCESS_MANAGER;
+        }
+
+        registerFilterSecurityInterceptor(element, parserContext, accessManagerId, fidsBuilder.getBeanDefinition());
 
         boolean sessionControlEnabled = registerConcurrentSessionControlBeansIfRequired(element, parserContext);
 
@@ -303,8 +342,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
         ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.EXCEPTION_TRANSLATION_FILTER));
     }
 
-    private void registerFilterSecurityInterceptor(Element element, ParserContext pc, UrlMatcher matcher,
-            String accessManagerId, LinkedHashMap<RequestKey, List<ConfigAttribute>> filterInvocationDefinitionMap) {
+    private void registerFilterSecurityInterceptor(Element element, ParserContext pc, String accessManagerId,
+            BeanDefinition fids) {
         BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
 
         builder.addPropertyReference("accessDecisionManager", accessManagerId);
@@ -314,10 +353,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
             builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
         }
 
-        DefaultFilterInvocationDefinitionSource fids =
-            new DefaultFilterInvocationDefinitionSource(matcher, filterInvocationDefinitionMap);
-        fids.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
-
         builder.addPropertyValue("objectDefinitionSource", fids);
         pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, builder.getBeanDefinition());
         ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FILTER_SECURITY_INTERCEPTOR));
@@ -635,7 +670,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
 
             if (useExpressions) {
                 logger.info("Creating access control expression attribute '" + access + "' for " + key);
-
+                attributes = new ArrayList<ConfigAttribute>(1);
+                // The expression will be parsed later by the ExpressionFilterInvocationDefinitionSource
+                attributes.add(new SecurityConfig(access));
 
             } else {
                 attributes = SecurityConfig.createList(StringUtils.commaDelimitedListToStringArray(access));

+ 7 - 2
core/src/main/java/org/springframework/security/expression/SecurityExpressionHandler.java

@@ -3,6 +3,7 @@ package org.springframework.security.expression;
 import org.aopalliance.intercept.MethodInvocation;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
 import org.springframework.security.Authentication;
 import org.springframework.security.intercept.web.FilterInvocation;
 
@@ -15,14 +16,18 @@ import org.springframework.security.intercept.web.FilterInvocation;
  * @since 2.5
  */
 public interface SecurityExpressionHandler {
+    /**
+     * @return an expression parser for the expressions used by the implementation.
+     */
+    ExpressionParser getExpressionParser();
 
     /**
-     * Provides a evaluation context in which to evaluate security expressions for a method invocation.
+     * Provides an evaluation context in which to evaluate security expressions for a method invocation.
      */
     EvaluationContext createEvaluationContext(Authentication authentication, MethodInvocation mi);
 
     /**
-     * Provides a evaluation context in which to evaluate security expressions for a web invocation.
+     * Provides an evaluation context in which to evaluate security expressions for a web invocation.
      */
     EvaluationContext createEvaluationContext(Authentication authentication, FilterInvocation fi);
 

+ 15 - 2
core/src/main/java/org/springframework/security/expression/method/ExpressionAnnotationMethodDefinitionSource.java

@@ -13,6 +13,7 @@ import org.springframework.expression.ParseException;
 import org.springframework.expression.spel.SpelExpressionParser;
 import org.springframework.security.ConfigAttribute;
 import org.springframework.security.config.SecurityConfigurationException;
+import org.springframework.security.expression.SecurityExpressionHandler;
 import org.springframework.security.expression.annotation.PostAuthorize;
 import org.springframework.security.expression.annotation.PostFilter;
 import org.springframework.security.expression.annotation.PreAuthorize;
@@ -38,7 +39,19 @@ import org.springframework.util.ClassUtils;
  * @version $Id$
  */
 public class ExpressionAnnotationMethodDefinitionSource extends AbstractMethodDefinitionSource {
-    private ExpressionParser parser = new SpelExpressionParser();
+    private ExpressionParser parser;
+
+    public ExpressionAnnotationMethodDefinitionSource() {
+        parser = new SpelExpressionParser();
+    }
+
+    /**
+     * Constructor which obtains the expression parser from the {@link SecurityExpressionHandler#getExpressionParser() }
+     * method on the supplied <tt>SecurityExpressionHandler</tt>.
+     */
+    public ExpressionAnnotationMethodDefinitionSource(SecurityExpressionHandler handler) {
+        parser = handler.getExpressionParser();
+    }
 
     public List<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
         if (method.getDeclaringClass() == Object.class) {
@@ -67,7 +80,7 @@ public class ExpressionAnnotationMethodDefinitionSource extends AbstractMethodDe
      * for the logic of this method. The ordering here is slightly different in that we consider method-specific
      * annotations on an interface before class-level ones.
      */
-    private <A  extends Annotation> A findAnnotation(Method method, Class targetClass, Class<A> annotationClass) {
+    private <A  extends Annotation> A findAnnotation(Method method, Class<?> targetClass, Class<A> annotationClass) {
         // The method may be on an interface, but we need attributes from the target class.
         // If the target class is null, the method will be unchanged.
         Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);

+ 10 - 1
core/src/main/java/org/springframework/security/expression/support/DefaultSecurityExpressionHandler.java

@@ -12,6 +12,8 @@ import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
 import org.springframework.core.ParameterNameDiscoverer;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.SpelExpressionParser;
 import org.springframework.expression.spel.standard.StandardEvaluationContext;
 import org.springframework.security.Authentication;
 import org.springframework.security.AuthenticationTrustResolver;
@@ -24,7 +26,7 @@ import org.springframework.security.intercept.web.FilterInvocation;
 /**
  * The standard implementation of <tt>SecurityExpressionHandler</tt>.
  * <p>
- * A single instance should usually be shared.
+ * A single instance should usually be shared amongst the beans that require expression support.
  *
  * @author Luke Taylor
  * @version $Id$
@@ -37,6 +39,7 @@ public class DefaultSecurityExpressionHandler implements SecurityExpressionHandl
     private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
     private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
     private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
+    private ExpressionParser expressionParser = new SpelExpressionParser();
 
     public DefaultSecurityExpressionHandler() {
     }
@@ -57,6 +60,8 @@ public class DefaultSecurityExpressionHandler implements SecurityExpressionHandl
 
     public EvaluationContext createEvaluationContext(Authentication authentication, FilterInvocation fi) {
         StandardEvaluationContext ctx = new StandardEvaluationContext();
+        SecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, fi);
+        ctx.setRootObject(root);
 
         return ctx;
     }
@@ -127,6 +132,10 @@ public class DefaultSecurityExpressionHandler implements SecurityExpressionHandl
         throw new IllegalArgumentException("Filter target must be a collection or array type, but was " + filterTarget);
     }
 
+    public ExpressionParser getExpressionParser() {
+        return expressionParser;
+    }
+
     public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
         this.parameterNameDiscoverer = parameterNameDiscoverer;
     }

+ 0 - 50
core/src/main/java/org/springframework/security/expression/support/MethodInvocationSecurityExpressionRoot.java

@@ -1,50 +0,0 @@
-package org.springframework.security.expression.support;
-
-import java.io.Serializable;
-
-import org.springframework.security.Authentication;
-import org.springframework.security.expression.PermissionEvaluator;
-
-public class MethodInvocationSecurityExpressionRoot extends SecurityExpressionRoot {
-    private PermissionEvaluator permissionEvaluator;
-    private Object filterObject;
-    private Object returnObject;
-    public final String read = "read";
-    public final String write = "write";
-    public final String create = "create";
-    public final String delete = "delete";
-    public final String admin = "administration";
-
-    MethodInvocationSecurityExpressionRoot(Authentication a) {
-        super(a);
-    }
-
-    public boolean hasPermission(Object target, Object permission) {
-        return permissionEvaluator.hasPermission(authentication, target, permission);
-    }
-
-    public boolean hasPermission(Object targetId, String targetType, Object permission) {
-        return permissionEvaluator.hasPermission(authentication, (Serializable)targetId, targetType, permission);
-    }
-
-    public void setFilterObject(Object filterObject) {
-        this.filterObject = filterObject;
-    }
-
-    public Object getFilterObject() {
-        return filterObject;
-    }
-
-    public void setReturnObject(Object returnObject) {
-        this.returnObject = returnObject;
-    }
-
-    public Object getReturnObject() {
-        return returnObject;
-    }
-
-    public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
-        this.permissionEvaluator = permissionEvaluator;
-    }
-
-}

+ 19 - 0
core/src/main/java/org/springframework/security/expression/support/WebSecurityExpressionRoot.java

@@ -0,0 +1,19 @@
+package org.springframework.security.expression.support;
+
+import org.springframework.security.Authentication;
+import org.springframework.security.intercept.web.FilterInvocation;
+
+/**
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+class WebSecurityExpressionRoot extends SecurityExpressionRoot {
+    private FilterInvocation filterInvocation;
+
+    WebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
+        super(a);
+        this.filterInvocation = fi;
+    }
+}

+ 60 - 0
core/src/main/java/org/springframework/security/expression/web/ExpressionBasedFilterInvocationDefinitionSource.java

@@ -0,0 +1,60 @@
+package org.springframework.security.expression.web;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.ParseException;
+import org.springframework.security.ConfigAttribute;
+import org.springframework.security.expression.SecurityExpressionHandler;
+import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;
+import org.springframework.security.intercept.web.RequestKey;
+import org.springframework.security.util.UrlMatcher;
+import org.springframework.util.Assert;
+
+/**
+ * Expression-based <tt>FilterInvocationDefinitionSource</tt>.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+public final class ExpressionBasedFilterInvocationDefinitionSource extends DefaultFilterInvocationDefinitionSource {
+    private final static Log logger = LogFactory.getLog(ExpressionBasedFilterInvocationDefinitionSource.class);
+
+    public ExpressionBasedFilterInvocationDefinitionSource(UrlMatcher urlMatcher,
+            LinkedHashMap<RequestKey, List<ConfigAttribute>> requestMap, SecurityExpressionHandler expressionHandler) {
+        super(urlMatcher, processMap(requestMap, expressionHandler.getExpressionParser()));
+        Assert.notNull(expressionHandler, "A non-null SecurityExpressionHandler is required");
+    }
+
+    private static LinkedHashMap<RequestKey, List<ConfigAttribute>> processMap(
+            LinkedHashMap<RequestKey, List<ConfigAttribute>> requestMap, ExpressionParser parser) {
+        Assert.notNull(parser, "SecurityExpressionHandler returned a null parser object");
+
+        LinkedHashMap<RequestKey, List<ConfigAttribute>> requestToExpressionAttributesMap =
+            new LinkedHashMap<RequestKey, List<ConfigAttribute>>(requestMap);
+
+        for (Map.Entry<RequestKey, List<ConfigAttribute>> entry : requestMap.entrySet()) {
+            RequestKey request = entry.getKey();
+            Assert.isTrue(entry.getValue().size() == 1, "Expected a single expression attribute for " + request);
+            ArrayList<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>(1);
+            String expression = entry.getValue().get(0).getAttribute();
+            logger.debug("Adding web access control expression '" + expression + "', for " + request);
+            try {
+                attributes.add(new WebExpressionConfigAttribute(parser.parseExpression(expression)));
+            } catch (ParseException e) {
+                throw new IllegalArgumentException("Failed to parse expression '" + expression + "'");
+            }
+
+            requestToExpressionAttributesMap.put(request, attributes);
+        }
+
+        return requestToExpressionAttributesMap;
+    }
+
+}

+ 0 - 6
core/src/main/java/org/springframework/security/expression/web/WebExpressionConfigAttribute.java

@@ -1,8 +1,6 @@
 package org.springframework.security.expression.web;
 
 import org.springframework.expression.Expression;
-import org.springframework.expression.ParseException;
-import org.springframework.expression.spel.SpelExpressionParser;
 import org.springframework.security.ConfigAttribute;
 
 /**
@@ -15,10 +13,6 @@ import org.springframework.security.ConfigAttribute;
 class WebExpressionConfigAttribute implements ConfigAttribute {
     private final Expression authorizeExpression;
 
-    public WebExpressionConfigAttribute(String authorizeExpression) throws ParseException {
-        this.authorizeExpression = new SpelExpressionParser().parseExpression(authorizeExpression);
-    }
-
     public WebExpressionConfigAttribute(Expression authorizeExpression) {
         this.authorizeExpression = authorizeExpression;
     }

+ 31 - 12
core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java

@@ -1,11 +1,6 @@
 package org.springframework.security.config;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.*;
 import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_XML;
 
 import java.lang.reflect.Method;
@@ -19,21 +14,24 @@ import org.junit.Test;
 import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
 import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.mock.web.MockFilterChain;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.mock.web.MockHttpSession;
+import org.springframework.security.AccessDeniedException;
 import org.springframework.security.ConfigAttribute;
 import org.springframework.security.MockAuthenticationEntryPoint;
-import org.springframework.security.MockFilterChain;
 import org.springframework.security.SecurityConfig;
 import org.springframework.security.concurrent.ConcurrentLoginException;
 import org.springframework.security.concurrent.ConcurrentSessionControllerImpl;
 import org.springframework.security.concurrent.ConcurrentSessionFilter;
 import org.springframework.security.context.HttpSessionSecurityContextRepository;
+import org.springframework.security.context.SecurityContextHolder;
 import org.springframework.security.context.SecurityContextPersistenceFilter;
 import org.springframework.security.intercept.web.FilterInvocation;
 import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
 import org.springframework.security.intercept.web.FilterSecurityInterceptor;
+import org.springframework.security.providers.TestingAuthenticationToken;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.security.providers.anonymous.AnonymousProcessingFilter;
 import org.springframework.security.securechannel.ChannelProcessingFilter;
@@ -69,6 +67,7 @@ public class HttpSecurityBeanDefinitionParserTests {
             appContext.close();
             appContext = null;
         }
+        SecurityContextHolder.clearContext();
     }
 
     @Test
@@ -691,18 +690,38 @@ public class HttpSecurityBeanDefinitionParserTests {
     }
 
     @Test
-    public void createDefinedSecurityContextRepository() throws Exception {
+    public void expressionBasedAccessAllowsAndDeniesAccessAsExpected() throws Exception {
         setContext(
-                "<b:bean id='repo' class='org.springframework.security.context.HttpSessionSecurityContextRepository'/>" +
-                "<http security-context-repository-ref='repo'>" +
-                "    <http-basic />" +
-                "</http>" + AUTH_PROVIDER_XML);
+                "    <http auto-config='true' use-expressions='true'>" +
+                "        <intercept-url pattern='/secure*' access=\"hasRole('ROLE_A')\" />" +
+                "        <intercept-url pattern='/**' access='permitAll()' />" +
+                "    </http>" + AUTH_PROVIDER_XML);
+
+        FilterSecurityInterceptor fis = (FilterSecurityInterceptor) appContext.getBean(BeanIds.FILTER_SECURITY_INTERCEPTOR);
+
+        FilterInvocationDefinitionSource fids = fis.getObjectDefinitionSource();
+        List<? extends ConfigAttribute> attrDef = fids.getAttributes(createFilterinvocation("/secure", null));
+        assertEquals(1, attrDef.size());
+
+        // Try an unprotected invocation
+        SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("joe", "", "ROLE_A"));
+        fis.invoke(createFilterinvocation("/permitallurl", null));
+        // Try secure Url as a valid user
+        fis.invoke(createFilterinvocation("/securex", null));
+        // And as a user without the required role
+        SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("joe", "", "ROLE_B"));
+        try {
+            fis.invoke(createFilterinvocation("/securex", null));
+            fail("Expected AccessDeniedInvocation");
+        } catch (AccessDeniedException expected) {
+        }
     }
 
     private void setContext(String context) {
         appContext = new InMemoryXmlApplicationContext(context);
     }
 
+    @SuppressWarnings("unchecked")
     private List<Filter> getFilters(String url) throws Exception {
         FilterChainProxy fcp = (FilterChainProxy) appContext.getBean(BeanIds.FILTER_CHAIN_PROXY);
         Method getFilters = fcp.getClass().getDeclaredMethod("getFilters", String.class);