SEC-2151: Support binding method arguments with Annotations

This allow utilizing method arguments for method access control on
interfaces prior to JDK 8.
This commit is contained in:
Rob Winch 2013-09-27 11:18:37 -05:00
parent fb0a8d19e8
commit a09756745f
9 changed files with 547 additions and 36 deletions

View File

@ -166,4 +166,37 @@ public class GlobalMethodSecurityConfigurationTests extends BaseSpringSpec {
grantAccess
}
}
def "Method Security supports annotations on interface parameter names"() {
setup:
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken("user", "password","ROLE_USER"))
loadConfig(MethodSecurityServiceConfig)
MethodSecurityService service = context.getBean(MethodSecurityService)
when: "service with annotated argument"
service.postAnnotation('deny')
then: "properly throws AccessDeniedException"
thrown(AccessDeniedException)
when: "service with annotated argument"
service.postAnnotation('grant')
then: "properly throws AccessDeniedException"
noExceptionThrown()
}
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodSecurityServiceConfig extends GlobalMethodSecurityConfiguration {
@Override
protected void registerAuthentication(AuthenticationManagerBuilder auth)
throws Exception {
auth
.inMemoryAuthentication()
}
@Bean
public MethodSecurityService service() {
new MethodSecurityServiceImpl()
}
}
}

View File

@ -19,6 +19,7 @@ import javax.annotation.security.DenyAll
import javax.annotation.security.PermitAll;
import org.springframework.security.access.annotation.Secured
import org.springframework.security.access.method.P
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.Authentication
@ -55,4 +56,7 @@ public interface MethodSecurityService {
@PostAuthorize("hasPermission(#object,'read')")
public String postHasPermission(String object);
@PostAuthorize("#o?.contains('grant')")
public String postAnnotation(@P("o") String object);
}

View File

@ -69,4 +69,9 @@ public class MethodSecurityServiceImpl implements MethodSecurityService {
public String postHasPermission(String object) {
return null;
}
@Override
public String postAnnotation(String object) {
return null;
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.springframework.security.access.method;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.security.core.parameters.AnnotationParameterNameDiscoverer;
/**
* An annotation that can be used along with
* {@link AnnotationParameterNameDiscoverer} to specify parameter names. This is
* useful for interfaces prior to JDK 8 which cannot contain the parameter
* names.
*
* @see AnnotationParameterNameDiscoverer
*
* @author Rob Winch
* @since 3.2
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface P {
/**
* The parameter name
* @return
*/
String value();
}

View File

@ -0,0 +1,228 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.core.parameters;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.PrioritizedParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.access.method.P;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* Allows finding parameter names using the value attribute of any number of
* {@link Annotation} instances. This is useful when needing to discover the
* parameter names of interfaces with Spring Security's method level security.
* For example, consider the following:
*
* <pre>
* import org.springframework.security.access.method.P;
*
* @PostAuthorize("#to == returnObject.to")
* public Message findMessageByTo(@P("to") String to);
* </pre>
*
* We can make this possible using the following
* {@link AnnotationParameterNameDiscoverer}:
*
* <pre>
* ParameterAnnotationsNameDiscoverer discoverer = new ParameterAnnotationsNameDiscoverer(
* &quot;org.springframework.security.access.method.P&quot;);
* </pre>
*
* <p>
* It is common for users to use {@link AnnotationParameterNameDiscoverer} in
* conjuction with {@link PrioritizedParameterNameDiscoverer}. In fact, Spring
* Security's {@link DefaultSecurityParameterNameDiscoverer} (which is used by
* default with method level security) extends
* {@link PrioritizedParameterNameDiscoverer} and will automatically support
* both {@link P} and Spring Data's Param annotation if it is found on the
* classpath.
* </p>
*
* <p>
* It is important that all the parameter names have a supported annotation on
* them. Otherwise, the result will be null. For example, consider the
* following:
* </p>
*
* <pre>
* import org.springframework.security.access.method.P;
*
* @PostAuthorize("#to == returnObject.to")
* public Message findMessageByToAndFrom(@P("to") User to, User from);
* </pre>
*
* <p>
* The result of finding parameters on the previous sample will be a null
* String[] since only a single parameter contains an annotation. This is mostly
* due to the fact that the fallbacks for
* {@link PrioritizedParameterNameDiscoverer} are an all or nothing operation.
* </p>
*
* @see DefaultSecurityParameterNameDiscoverer
*
* @author Rob Winch
* @since 3.2
*/
public class AnnotationParameterNameDiscoverer implements
ParameterNameDiscoverer {
private final Set<String> annotationClassesToUse;
public AnnotationParameterNameDiscoverer(String... annotationClassToUse) {
this(new HashSet<String>(Arrays.asList(annotationClassToUse)));
}
public AnnotationParameterNameDiscoverer(Set<String> annotationClassesToUse) {
Assert.notEmpty(annotationClassesToUse,
"annotationClassesToUse cannot be null or empty");
this.annotationClassesToUse = annotationClassesToUse;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.core.ParameterNameDiscoverer#getParameterNames(java
* .lang.reflect.Method)
*/
public String[] getParameterNames(Method method) {
Method originalMethod = BridgeMethodResolver.findBridgedMethod(method);
String[] paramNames = lookupParameterNames(METHOD_METHODPARAM_FACTORY, originalMethod);
if(paramNames != null) {
return paramNames;
}
Class<?> declaringClass = method.getDeclaringClass();
Class<?>[] interfaces = declaringClass.getInterfaces();
for(Class<?> intrfc : interfaces) {
Method intrfcMethod = ReflectionUtils.findMethod(intrfc, method.getName(), method.getParameterTypes());
if(intrfcMethod != null) {
return lookupParameterNames(METHOD_METHODPARAM_FACTORY, intrfcMethod);
}
}
return paramNames;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.core.ParameterNameDiscoverer#getParameterNames(java
* .lang.reflect.Constructor)
*/
public String[] getParameterNames(Constructor<?> constructor) {
return lookupParameterNames(CONSTRUCTOR_METHODPARAM_FACTORY,
constructor);
}
/**
* Gets the parameter names or null if not found.
*
* @param parameterNameFactory the {@link ParameterNameFactory} to use
* @param t the {@link AccessibleObject} to find the parameter names on (i.e. Method or Constructor)
* @return the parameter names or null
*/
private <T extends AccessibleObject> String[] lookupParameterNames(
ParameterNameFactory<T> parameterNameFactory, T t) {
int parameterCount = parameterNameFactory.getParamCount(t);
String[] paramNames = new String[parameterCount];
for (int i = 0; i < parameterCount; i++) {
Annotation[] annotations = parameterNameFactory.findAnnotationsAt(t, i);
String parameterName = findParameterName(annotations);
if (parameterName == null) {
return null;
} else {
paramNames[i] = parameterName;
}
}
return paramNames;
}
/**
* Finds the parameter name from the provided {@link Annotation}s or null if
* it could not find it. The search is done by looking at the value property
* of the {@link #annotationClassesToUse}.
*
* @param parameterAnnotations
* the {@link Annotation}'s to search.
* @return
*/
private String findParameterName(Annotation[] parameterAnnotations) {
for (Annotation paramAnnotation : parameterAnnotations) {
if (annotationClassesToUse.contains(paramAnnotation
.annotationType().getName())) {
return (String) AnnotationUtils.getValue(paramAnnotation,
"value");
}
}
return null;
}
private static final ParameterNameFactory<Constructor<?>> CONSTRUCTOR_METHODPARAM_FACTORY = new ParameterNameFactory<Constructor<?>>() {
public int getParamCount(Constructor<?> constructor) {
return constructor.getParameterTypes().length;
}
public Annotation[] findAnnotationsAt(Constructor<?> constructor, int index) {
return constructor.getParameterAnnotations()[index];
}
};
private static final ParameterNameFactory<Method> METHOD_METHODPARAM_FACTORY = new ParameterNameFactory<Method>() {
public int getParamCount(Method method) {
return method.getParameterTypes().length;
}
public Annotation[] findAnnotationsAt(Method method, int index) {
return method.getParameterAnnotations()[index];
}
};
/**
* Strategy interface for looking up the parameter names.
*
* @author Rob Winch
* @since 3.2
*
* @param <T> the type to inspect (i.e. {@link Method} or {@link Constructor})
*/
private interface ParameterNameFactory<T extends AccessibleObject> {
/**
* Gets the parameter count
* @param t
* @return
*/
int getParamCount(T t);
/**
* Gets the {@link Annotation}s at a specified index
* @param t
* @param index
* @return
*/
Annotation[] findAnnotationsAt(T t, int index);
}
}

View File

@ -16,13 +16,16 @@
package org.springframework.security.core.parameters;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.PrioritizedParameterNameDiscoverer;
import org.springframework.security.access.method.P;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@ -33,6 +36,9 @@ import org.springframework.util.ClassUtils;
* classpath.
*
* <ul>
* <li>Will use an instance of {@link AnnotationParameterNameDiscoverer} with
* {@link P} as a valid annotation. If, Spring Data is on the classpath will
* also add Param annotation.</li>
* <li>If Spring 4 is on the classpath, then DefaultParameterNameDiscoverer is
* added. This attempts to use JDK 8 information first and falls back to
* {@link LocalVariableTableParameterNameDiscoverer}.</li>
@ -40,6 +46,8 @@ import org.springframework.util.ClassUtils;
* {@link LocalVariableTableParameterNameDiscoverer} is added directly.</li>
* </ul>
*
* @see AnnotationParameterNameDiscoverer
*
* @author Rob Winch
* @since 3.2
*/
@ -53,6 +61,10 @@ public class DefaultSecurityParameterNameDiscoverer extends
private static final boolean DEFAULT_PARAM_DISCOVERER_PRESENT =
ClassUtils.isPresent(DEFAULT_PARAMETER_NAME_DISCOVERER_CLASSNAME, DefaultSecurityParameterNameDiscoverer.class.getClassLoader());
private static final String DATA_PARAM_CLASSNAME = "org.springframework.data.repository.query.Param";
private static final boolean DATA_PARAM_PRESENT =
ClassUtils.isPresent(DATA_PARAM_CLASSNAME, DefaultSecurityParameterNameDiscoverer.class.getClassLoader());
/**
* Creates a new instance with only the default
* {@link ParameterNameDiscoverer} instances.
@ -71,6 +83,15 @@ public class DefaultSecurityParameterNameDiscoverer extends
for(ParameterNameDiscoverer discover : parameterNameDiscovers) {
addDiscoverer(discover);
}
Set<String> annotationClassesToUse = new HashSet<String>(2);
annotationClassesToUse.add(P.class.getName());
if(DATA_PARAM_PRESENT) {
annotationClassesToUse.add(DATA_PARAM_CLASSNAME);
}
addDiscoverer(new AnnotationParameterNameDiscoverer(annotationClassesToUse));
if (DEFAULT_PARAM_DISCOVERER_PRESENT) {
try {
Class<? extends ParameterNameDiscoverer> paramNameDiscoverClass = (Class<? extends ParameterNameDiscoverer>) ClassUtils

View File

@ -0,0 +1,106 @@
package org.springframework.security.core.parameters;
import static org.fest.assertions.Assertions.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.access.method.P;
import org.springframework.util.ReflectionUtils;
public class AnnotationParameterNameDiscovererTests {
private AnnotationParameterNameDiscoverer discoverer;
@Before
public void setup() {
discoverer = new AnnotationParameterNameDiscoverer(P.class.getName());
}
@Test
public void getParameterNamesInterfaceSingleParam() {
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(Dao.class, "findMessageByTo", String.class))).isEqualTo(new String [] { "to"});
}
@Test
public void getParameterNamesInterfaceSingleParamAnnotatedWithMultiParams() {
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(Dao.class, "findMessageByToAndFrom", String.class, String.class))).isNull();
}
@Test
public void getParameterNamesInterfaceNoAnnotation() {
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(Dao.class, "findMessageByIdNoAnnotation", String.class))).isNull();
}
@Test
public void getParameterNamesClassSingleParam() {
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(Dao.class, "findMessageByTo", String.class))).isEqualTo(new String [] { "to"});
}
@Test
public void getParameterNamesClassSingleParamAnnotatedWithMultiParams() {
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(Dao.class, "findMessageByToAndFrom", String.class, String.class))).isNull();
}
@Test
public void getParameterNamesClassNoAnnotation() {
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(Dao.class, "findMessageByIdNoAnnotation", String.class))).isNull();
}
@Test
public void getParameterNamesConstructor() throws Exception {
assertThat(discoverer.getParameterNames(Impl.class.getConstructor(String.class))).isEqualTo(new String[] { "id"});
}
@Test
public void getParameterNamesConstructorNoAnnotation() throws Exception {
assertThat(discoverer.getParameterNames(Impl.class.getConstructor(Long.class))).isNull();
}
@Test
public void getParameterNamesClassAnnotationOnInterface() throws Exception {
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(DaoImpl.class, "findMessageByTo", String.class))).isEqualTo(new String[] {"to"});
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(Dao.class, "findMessageByTo", String.class))).isEqualTo(new String[] {"to"});
}
@Test
public void getParameterNamesClassAnnotationOnImpl() throws Exception {
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(Dao.class, "findMessageByToAndFrom", String.class, String.class))).isNull();
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(DaoImpl.class, "findMessageByToAndFrom", String.class, String.class))).isEqualTo(new String[] {"to", "from"});
}
@Test
public void getParameterNamesClassAnnotationOnBaseClass() throws Exception {
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(Dao.class, "findMessageByIdNoAnnotation", String.class))).isNull();
assertThat(discoverer.getParameterNames(ReflectionUtils.findMethod(DaoImpl.class, "findMessageByIdNoAnnotation", String.class))).isEqualTo(new String[] {"id"});
}
interface Dao {
String findMessageByTo(@P("to") String to);
String findMessageByToAndFrom(@P("to") String to, String from);
String findMessageByIdNoAnnotation(String id);
}
static class BaseDaoImpl {
public String findMessageByIdNoAnnotation(@P("id") String id) { return null; }
}
static class DaoImpl extends BaseDaoImpl implements Dao {
public String findMessageByTo(String to) { return null; }
public String findMessageByToAndFrom(@P("to") String to, @P("from") String from) { return null; }
}
static class Impl {
public Impl(Long dataSourceId) {}
public Impl(@P("id") String dataSourceId) {}
String findMessageByTo(@P("to") String to) { return null; }
String findMessageByToAndFrom(@P("to") String to, String from) { return null; }
String findMessageByIdNoAnnotation(String id) { return null; }
}
}

View File

@ -19,12 +19,14 @@ import static org.fest.assertions.Assertions.assertThat;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.security.access.method.P;
import org.springframework.test.util.ReflectionTestUtils;
/**
@ -45,8 +47,16 @@ public class DefaultSecurityParameterNameDiscovererTests {
public void constructorDefault() {
List<ParameterNameDiscoverer> discoverers = (List<ParameterNameDiscoverer>) ReflectionTestUtils
.getField(discoverer, "parameterNameDiscoverers");
assertThat(discoverers.size()).isEqualTo(1);
assertThat(discoverers.get(0)).isInstanceOf(
assertThat(discoverers.size()).isEqualTo(2);
ParameterNameDiscoverer annotationDisc = discoverers.get(0);
assertThat(annotationDisc).isInstanceOf(
AnnotationParameterNameDiscoverer.class);
Set<String> annotationsToUse = (Set<String>)ReflectionTestUtils.getField(annotationDisc, "annotationClassesToUse");
assertThat(annotationsToUse).containsOnly(P.class.getName());
assertThat(discoverers.get(1)).isInstanceOf(
DefaultParameterNameDiscoverer.class);
}
@ -57,10 +67,17 @@ public class DefaultSecurityParameterNameDiscovererTests {
List<ParameterNameDiscoverer> discoverers = (List<ParameterNameDiscoverer>) ReflectionTestUtils
.getField(discoverer, "parameterNameDiscoverers");
assertThat(discoverers.size()).isEqualTo(2);
assertThat(discoverers.size()).isEqualTo(3);
assertThat(discoverers.get(0)).isInstanceOf(
LocalVariableTableParameterNameDiscoverer.class);
assertThat(discoverers.get(1)).isInstanceOf(
ParameterNameDiscoverer annotationDisc = discoverers.get(1);
assertThat(annotationDisc).isInstanceOf(
AnnotationParameterNameDiscoverer.class);
Set<String> annotationsToUse = (Set<String>)ReflectionTestUtils.getField(annotationDisc, "annotationClassesToUse");
assertThat(annotationsToUse).containsOnly(P.class.getName());
assertThat(discoverers.get(2)).isInstanceOf(
DefaultParameterNameDiscoverer.class);
}
}

View File

@ -139,38 +139,88 @@
application)<programlisting language="java">
@PreAuthorize("hasRole('ROLE_USER')")
public void create(Contact contact);</programlisting>which
means that access will only be allowed for users with the role "ROLE_USER".
Obviously the same thing could easily be achieved using a traditional
configuration and a simple configuration attribute for the required role. But
what
about:<programlisting language="java">
@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);</programlisting>Here
we're actually using a method argument as part of the expression to decide
whether the current user has the <quote>admin</quote>permission for the given
contact. The built-in <literal>hasPermission()</literal> expression is linked
into the Spring Security ACL module through the application context, as we'll
<link linkend="el-permission-evaluator">see below</link>. You can access any
of the method arguments by name as expression variables, provided your code has
debug information compiled in. Any Spring-EL functionality is available within
the expression, so you can also access properties on the arguments. For example,
if you wanted a particular method to only allow access to a user whose username
matched that of the contact, you could write</para>
<programlisting language="java">
@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);</programlisting>
<para>Here we are accessing another builtin expression, <literal>authentication</literal>,
which is the <interfacename>Authentication</interfacename> stored in the
security context. You can also access its <quote>principal</quote> property
directly, using the expression <literal>principal</literal>. The value will
often be a <interfacename>UserDetails</interfacename> instance, so you might use an
expression like <literal>principal.username</literal> or
<literal>principal.enabled</literal>.</para>
<para>Less commonly, you may wish to perform an access-control check after the
method has been invoked. This can be achieved using the
<literal>@PostAuthorize</literal> annotation. To access the return value from a
method, use the builtin name <literal>returnObject</literal> in the
expression.</para>
means that access will only be allowed for users with the role "ROLE_USER".</para>
<section xml:id="el-pre-post-annotations-arguments">
<title>Resolving method arguments</title>
<para>Obviously the same thing could easily be achieved using a traditional
configuration and a simple configuration attribute for the required role. But
what
about:<programlisting language="java">
@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);</programlisting>Here
we're actually using a method argument as part of the expression to decide
whether the current user has the <quote>admin</quote>permission for the given
contact. The built-in <literal>hasPermission()</literal> expression is linked
into the Spring Security ACL module through the application context, as we'll
<link linkend="el-permission-evaluator">see below</link>. You can access any
of the method arguments by name as expression variables.</para>
<para>There are a number of ways in which Spring Security can resolve the method arguments. Spring Security
uses <classname>DefaultSecurityParameterNameDiscoverer</classname> to discover the parameter names. By default,
the following options are tried for a method as a whole.
<orderedlist inheritnum="ignore" continuation="restarts">
<listitem>
<para>If Spring Security's <literal>@P</literal> annotation is present on a single argument to the method,
the value will be used. This is useful for interfaces compiled with a JDK prior to JDK 8 which do not contain
any information about the parameter names. For example: <programlisting language="java">
import org.springframework.security.access.method.P;
...
@PreAuthorize("#c.name == authentication.name")
public void doSomething(@P("c") Contact contact);</programlisting></para>
<para>Behind the scenes this use implemented using <classname>AnnotationParameterNameDiscoverer</classname> which
can be customized to support the value attribute of any specified annotation.</para>
</listitem>
<listitem>
<para>If Spring Data's <literal>@Param</literal> annotation is present on at least one parameter for the method,
the value will be used. This is useful for interfaces compiled with a JDK prior to JDK 8 which do not contain
any information about the parameter names. For example: <programlisting language="java">
import org.springframework.data.repository.query.Param;
...
@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);</programlisting></para>
<para>Behind the scenes this use implemented using <classname>AnnotationParameterNameDiscoverer</classname> which
can be customized to support the value attribute of any specified annotation.</para>
</listitem>
<listitem>
<para>If JDK 8 was used to compile the source with the -parameters argument and Spring 4+ is being used, then
the standard JDK reflection API is used to discover the parameter names. This works on both classes and
interfaces.</para>
</listitem>
<listitem>
<para>Last, if the code was compiled with the debug symbols, the parameter names will be discovered using
the debug symbols. This will not work for interfaces since they do not have debug information about the
parameter names. For interfaces, annotations or the JDK 8 approach must be used.</para>
</listitem>
</orderedlist></para>
</section>
<section xml:id="el-pre-post-annotations-spel">
<title>Method Expressions and SpEL</title>
<para>Any Spring-EL functionality is available within
the expression, so you can also access properties on the arguments. For example,
if you wanted a particular method to only allow access to a user whose username
matched that of the contact, you could write</para>
<programlisting language="java">
@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);</programlisting>
<para>Here we are accessing another builtin expression, <literal>authentication</literal>,
which is the <interfacename>Authentication</interfacename> stored in the
security context. You can also access its <quote>principal</quote> property
directly, using the expression <literal>principal</literal>. The value will
often be a <interfacename>UserDetails</interfacename> instance, so you might use an
expression like <literal>principal.username</literal> or
<literal>principal.enabled</literal>.</para>
</section>
<section xml:id="el-pre-post-annotations-post">
<title>Accessing the return value</title>
<para>Less commonly, you may wish to perform an access-control check after the
method has been invoked. This can be achieved using the
<literal>@PostAuthorize</literal> annotation. To access the return value from a
method, use the builtin name <literal>returnObject</literal> in the
expression.</para>
</section>
</section>
<section>
<title>Filtering using <literal>@PreFilter</literal> and