1
0
mirror of synced 2026-08-04 09:17:02 +00:00

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
+82 -32
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