mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-13 15:42:25 +00:00
242 lines
17 KiB
XML
242 lines
17 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="el-access"
|
||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||
<title>Expression-Based Access Control</title>
|
||
<para> Spring Security 3.0 introduced the ability to use Spring EL expressions as an
|
||
authorization mechanism in addition to the simple use of configuration attributes and
|
||
access-decision voters which have seen before. Expression-based access control is built on
|
||
the same architecture but allows complicated boolean logic to be encapsulated in a single
|
||
expression.</para>
|
||
<section>
|
||
<title>Overview</title>
|
||
<para>Spring Security uses Spring EL for expression support and you should look at how that
|
||
works if you are interested in understanding the topic in more depth. Expressions are
|
||
evaluated with a <quote>root object</quote> as part of the evaluation context. Spring
|
||
Security uses specific classes for web and method security as the root object, in order
|
||
to provide built-in expressions and access to values such as the current
|
||
principal.</para>
|
||
<section xml:id="el-common-built-in">
|
||
<title>Common Built-In Expressions</title>
|
||
<para>The base class for expression root objects is
|
||
<classname>SecurityExpressionRoot</classname>. This provides some common
|
||
expressions which are available in both web and method security.</para>
|
||
<table frame="none">
|
||
<title>Common built-in expressions</title>
|
||
<tgroup cols="2">
|
||
<colspec colname="c1" colnum="1" colwidth="1.0*"/>
|
||
<colspec colname="c2" colnum="2" colwidth="2.0*"/>
|
||
<thead>
|
||
<row>
|
||
<entry>Expression</entry>
|
||
<entry>Description</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry><literal>hasRole([role])</literal></entry>
|
||
<entry>Returns <literal>true</literal> if the current principal has the
|
||
specified role.</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>hasAnyRole([role1,role2])</literal></entry>
|
||
<entry>Returns <literal>true</literal> if the current principal has any
|
||
of the supplied roles (given as a comma-separated list of
|
||
strings)</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>principal</literal></entry>
|
||
<entry>Allows direct access to the principal object representing the
|
||
current user</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>authentication</literal></entry>
|
||
<entry>Allows direct access to the current
|
||
<interfacename>Authentication</interfacename> object obtained
|
||
from the <interfacename>SecurityContext</interfacename></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>permitAll</literal></entry>
|
||
<entry>Always evaluates to <literal>true</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>denyAll</literal></entry>
|
||
<entry>Always evaluates to <literal>false</literal></entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>isAnonymous()</literal></entry>
|
||
<entry>Returns <literal>true</literal> if the current principal is an
|
||
anonymous user</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>isRememberMe()</literal></entry>
|
||
<entry>Returns <literal>true</literal> if the current principal is a
|
||
remember-me user</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>isAuthenticated()</literal></entry>
|
||
<entry>Returns <literal>true</literal> if the user is not
|
||
anonymous</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>isFullyAuthenticated()</literal></entry>
|
||
<entry>Returns <literal>true</literal> if the user is not an anonymous
|
||
or a remember-me user</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
</section>
|
||
</section>
|
||
<section xml:id="el-access-web">
|
||
<title>Web Security Expressions</title>
|
||
<para> To use expressions to secure individual URLs, you would first need to set the
|
||
<literal>use-expressions</literal> attribute in the <literal><http></literal>
|
||
element to <literal>true</literal>. Spring Security will then expect the
|
||
<literal>access</literal> attributes of the <literal><intercept-url></literal>
|
||
elements to contain Spring EL expressions. The expressions should evaluate to a boolean,
|
||
defining whether access should be allowed or not. For example:<programlisting><![CDATA[
|
||
<http use-expressions="true">
|
||
<intercept-url pattern="/admin*"
|
||
access="hasRole('admin') and hasIpAddress('192.168.1.0/24')"/>
|
||
...
|
||
</http>
|
||
]]></programlisting>Here we have defined that the <quote>admin</quote> area of an application
|
||
(defined by the URL pattern) should only be available to users who have the granted
|
||
authority <quote>admin</quote> and whose IP address matches a local subnet. We've
|
||
already seen the built-in <literal>hasRole</literal> expression in the previous section.
|
||
The expression <literal>hasIpAddress</literal> is an additional built-in expression
|
||
which is specific to web security. It is defined by the
|
||
<classname>WebSecurityExpressionRoot</classname> class, an instance of which is used
|
||
as the expression root object when evaluation web-access expressions. This object also
|
||
directly exposed the <interfacename>HttpServletRequest</interfacename> object under the
|
||
name <literal>request</literal> so you can invoke the request directly in an
|
||
expression.</para>
|
||
<para>If expressions are being used, a <classname>WebExpressionVoter</classname> will be
|
||
added to the <interfacename>AccessDecisionManager</interfacename> which is used by the
|
||
namespace. So if you aren't using the namespace and want to use expressions, you will
|
||
have to add one of these to your configuration.</para>
|
||
</section>
|
||
<section>
|
||
<title>Method Security Expressions</title>
|
||
<para>Method security is a bit more complicated than a simple allow or deny rule. Spring
|
||
Security 3.0 introduced some new annotations in order to allow comprehensive support for
|
||
the use of expressions.</para>
|
||
<section xml:id="el-pre-post-annotations">
|
||
<title><literal>@Pre</literal> and <literal>@Post</literal> Annotations</title>
|
||
<para>There are four annotations which support expression attributes to allow pre and
|
||
post-invocation authorization checks and also to support filtering of submitted
|
||
collection arguments or return values. They are <literal>@PreAuthorize</literal>,
|
||
<literal>@PreFilter</literal>, <literal>@PostAuthorize</literal> and
|
||
<literal>@PostFilter</literal>. Their use is enabled through the
|
||
<literal>global-method-security</literal> namespace
|
||
element:<programlisting><![CDATA[<global-method-security pre-post-annotations="enabled"/>]]></programlisting></para>
|
||
<section>
|
||
<title>Access Control using <literal>@PreAuthorize</literal> and
|
||
<literal>@PostAuthorize</literal></title>
|
||
<para>The most obviously useful annotation is <literal>@PreAuthorize</literal> which
|
||
decides whether a method can actually be invoked or not. For example (from the
|
||
<quote>Contacts</quote> sample
|
||
application)<programlisting> @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> @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 xlink:href="#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>
|
||
@PreAuthorize("#contact.name == authentication.name")
|
||
public void doSomething(Contact contact);</programlisting>
|
||
<para>Here we are accessing another built–in 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 built–in name <literal>returnObject</literal> in the
|
||
expression.</para>
|
||
</section>
|
||
<section>
|
||
<title>Filtering using <literal>@PreFilter</literal> and
|
||
<literal>@PostFilter</literal></title>
|
||
<para>As you may already be aware, Spring Security supports filtering of collections
|
||
and arrays and this can now be achieved using expressions. This is most commonly
|
||
performed on the return value of a method. For
|
||
example:<programlisting> @PreAuthorize("hasRole('ROLE_USER')")
|
||
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
|
||
public List<Contact> getAll();</programlisting>When
|
||
using the <literal>@PostFilter</literal> annotation, Spring Security iterates
|
||
through the returned collection and removes any elements for which the supplied
|
||
expression is false. The name <literal>filterObject</literal> refers to the
|
||
current object in the collection. You can also filter before the method call,
|
||
using <literal>@PreFilter</literal>, though this is a less common requirement.
|
||
The syntax is just the same, but if there is more than one argument which is a
|
||
collection type then you have to select one by name using the
|
||
<literal>filterTarget</literal> property of this annotation.</para>
|
||
<para>Note that filtering is obviously not a substitute for tuning your data
|
||
retrieval queries. If you are filtering large collections and removing many of
|
||
the entries then this is likely to be inefficient.</para>
|
||
</section>
|
||
</section>
|
||
<section xml:id="el-method-built-in">
|
||
<title>Built-In Expressions</title>
|
||
<para>There are some built-in expressions which are specific to method security, which
|
||
we have already seen in use above. The <literal>filterTarget</literal> and
|
||
<literal>returnValue</literal> values are simple enough, but the use of the
|
||
<literal>hasPermission()</literal> expression warrants a closer look.</para>
|
||
<section xml:id="el-permission-evaluator">
|
||
<title>The <interfacename>PermissionEvaluator</interfacename> interface</title>
|
||
<para><literal>hasPermission()</literal> expressions are delegated to an instance of
|
||
<interfacename>PermissionEvaluator</interfacename>. It is intended to bridge
|
||
between the expression system and Spring Security's ACL system, allowing you to
|
||
specify authorization constraints on domain objects, based on abstract
|
||
permissions. It has no explicit dependencies on the ACL module, so you could
|
||
swap that out for an alternative implementation if required. The interface has
|
||
two methods:
|
||
<programlisting language="java"> boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission);
|
||
|
||
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);</programlisting>which
|
||
map directly to the available versions of the expression, with the exception
|
||
that the first argument (the <interfacename>Authentication</interfacename>
|
||
object) is not supplied. The first is used in situations where the domain
|
||
object, to which access is being controlled, is already loaded. Then expression
|
||
will return true if the current user has the given permission for that object.
|
||
The second version is used in cases where the object is not loaded, but its
|
||
identifier is known. An abstract <quote>type</quote> specifier for the domain
|
||
object is also required, allowing the correct ACL permissions to be loaded. This
|
||
has traditionally been the Java class of the object, but does not have to be as
|
||
long as it is consistent with how the permissions are loaded.</para>
|
||
<para>To use <literal>hasPermission()</literal> expressions, you have to explicitly
|
||
configure a <interfacename>PermissionEvaluator</interfacename> in your
|
||
application context. This would look something like this:<programlisting language="xml"> <![CDATA[ <security:global-method-security pre-post-annotations="enabled">
|
||
<security:expression-handler ref="expressionHandler"/>
|
||
</security:global-method-security>
|
||
|
||
<bean id="expressionHandler"
|
||
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
|
||
<property name="permissionEvaluator" ref="myPermissionEvaluator"/>
|
||
</bean>]]></programlisting>Where <literal>myPermissionEvaluator</literal> is the bean which
|
||
implements <interfacename>PermissionEvaluator</interfacename>. Usually this will
|
||
be the implementation from the ACL module which is called
|
||
<classname>AclPermissionEvaluator</classname>. See the
|
||
<quote>Contacts</quote> sample application configuration for more
|
||
details.</para>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
</chapter>
|