el-access.xml 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="el-access"
  3. xmlns:xlink="http://www.w3.org/1999/xlink">
  4. <title>Expression-Based Access Control</title>
  5. <para> Spring Security 3.0 introduced the ability to use Spring EL expressions as an
  6. authorization mechanism in addition to the simple use of configuration attributes and
  7. access-decision voters which have seen before. Expression-based access control is built on
  8. the same architecture but allows complicated boolean logic to be encapsulated in a single
  9. expression.</para>
  10. <section>
  11. <title>Overview</title>
  12. <para>Spring Security uses Spring EL for expression support and you should look at how that
  13. works if you are interested in understanding the topic in more depth. Expressions are
  14. evaluated with a <quote>root object</quote> as part of the evaluation context. Spring
  15. Security uses specific classes for web and method security as the root object, in order
  16. to provide built-in expressions and access to values such as the current
  17. principal.</para>
  18. <section xml:id="el-common-built-in">
  19. <title>Common Built-In Expressions</title>
  20. <para>The base class for expression root objects is
  21. <classname>SecurityExpressionRoot</classname>. This provides some common
  22. expressions which are available in both web and method security.</para>
  23. <table frame="none">
  24. <title>Common built-in expressions</title>
  25. <tgroup cols="2">
  26. <colspec colname="c1" colnum="1" colwidth="1.0*"/>
  27. <colspec colname="c2" colnum="2" colwidth="2.0*"/>
  28. <thead>
  29. <row>
  30. <entry>Expression</entry>
  31. <entry>Description</entry>
  32. </row>
  33. </thead>
  34. <tbody>
  35. <row>
  36. <entry><literal>hasRole([role])</literal></entry>
  37. <entry>Returns <literal>true</literal> if the current principal has the
  38. specified role.</entry>
  39. </row>
  40. <row>
  41. <entry><literal>hasAnyRole([role1,role2])</literal></entry>
  42. <entry>Returns <literal>true</literal> if the current principal has any
  43. of the supplied roles (given as a comma-separated list of
  44. strings)</entry>
  45. </row>
  46. <row>
  47. <entry><literal>principal</literal></entry>
  48. <entry>Allows direct access to the principal object representing the
  49. current user</entry>
  50. </row>
  51. <row>
  52. <entry><literal>authentication</literal></entry>
  53. <entry>Allows direct access to the current
  54. <interfacename>Authentication</interfacename> object obtained
  55. from the <interfacename>SecurityContext</interfacename></entry>
  56. </row>
  57. <row>
  58. <entry><literal>permitAll</literal></entry>
  59. <entry>Always evaluates to <literal>true</literal></entry>
  60. </row>
  61. <row>
  62. <entry><literal>denyAll</literal></entry>
  63. <entry>Always evaluates to <literal>false</literal></entry>
  64. </row>
  65. <row>
  66. <entry><literal>isAnonymous()</literal></entry>
  67. <entry>Returns <literal>true</literal> if the current principal is an
  68. anonymous user</entry>
  69. </row>
  70. <row>
  71. <entry><literal>isRememberMe()</literal></entry>
  72. <entry>Returns <literal>true</literal> if the current principal is a
  73. remember-me user</entry>
  74. </row>
  75. <row>
  76. <entry><literal>isAuthenticated()</literal></entry>
  77. <entry>Returns <literal>true</literal> if the user is not
  78. anonymous</entry>
  79. </row>
  80. <row>
  81. <entry><literal>isFullyAuthenticated()</literal></entry>
  82. <entry>Returns <literal>true</literal> if the user is not an anonymous
  83. or a remember-me user</entry>
  84. </row>
  85. </tbody>
  86. </tgroup>
  87. </table>
  88. </section>
  89. </section>
  90. <section xml:id="el-access-web">
  91. <title>Web Security Expressions</title>
  92. <para> To use expressions to secure individual URLs, you would first need to set the
  93. <literal>use-expressions</literal> attribute in the <literal>&lt;http></literal>
  94. element to <literal>true</literal>. Spring Security will then expect the
  95. <literal>access</literal> attributes of the <literal>&lt;intercept-url></literal>
  96. elements to contain Spring EL expressions. The expressions should evaluate to a boolean,
  97. defining whether access should be allowed or not. For example:<programlisting><![CDATA[
  98. <http use-expressions="true">
  99. <intercept-url pattern="/admin*"
  100. access="hasRole('admin') and hasIpAddress('192.168.1.0/24')"/>
  101. ...
  102. </http>
  103. ]]></programlisting>Here we have defined that the <quote>admin</quote> area of an application
  104. (defined by the URL pattern) should only be available to users who have the granted
  105. authority <quote>admin</quote> and whose IP address matches a local subnet. We've
  106. already seen the built-in <literal>hasRole</literal> expression in the previous section.
  107. The expression <literal>hasIpAddress</literal> is an additional built-in expression
  108. which is specific to web security. It is defined by the
  109. <classname>WebSecurityExpressionRoot</classname> class, an instance of which is used
  110. as the expression root object when evaluation web-access expressions. This object also
  111. directly exposed the <interfacename>HttpServletRequest</interfacename> object under the
  112. name <literal>request</literal> so you can invoke the request directly in an
  113. expression.</para>
  114. <para>If expressions are being used, a <classname>WebExpressionVoter</classname> will be
  115. added to the <interfacename>AccessDecisionManager</interfacename> which is used by the
  116. namespace. So if you aren't using the namespace and want to use expressions, you will
  117. have to add one of these to your configuration.</para>
  118. </section>
  119. <section>
  120. <title>Method Security Expressions</title>
  121. <para>Method security is a bit more complicated than a simple allow or deny rule. Spring
  122. Security 3.0 introduced some new annotations in order to allow comprehensive support for
  123. the use of expressions.</para>
  124. <section xml:id="el-pre-post-annotations">
  125. <title><literal>@Pre</literal> and <literal>@Post</literal> Annotations</title>
  126. <para>There are four annotations which support expression attributes to allow pre and
  127. post-invocation authorization checks and also to support filtering of submitted
  128. collection arguments or return values. They are <literal>@PreAuthorize</literal>,
  129. <literal>@PreFilter</literal>, <literal>@PostAuthorize</literal> and
  130. <literal>@PostFilter</literal>. Their use is enabled through the
  131. <literal>global-method-security</literal> namespace
  132. element:<programlisting><![CDATA[<global-method-security pre-post-annotations="enabled"/>]]></programlisting></para>
  133. <section>
  134. <title>Access Control using <literal>@PreAuthorize</literal> and
  135. <literal>@PostAuthorize</literal></title>
  136. <para>The most obviously useful annotation is <literal>@PreAuthorize</literal> which
  137. decides whether a method can actually be invoked or not. For example (from the
  138. <quote>Contacts</quote> sample
  139. application)<programlisting> @PreAuthorize("hasRole('ROLE_USER')")
  140. public void create(Contact contact);</programlisting>which
  141. means that access will only be allowed for users with the role "ROLE_USER".
  142. Obviously the same thing could easily be achieved using a traditional
  143. configuration and a simple configuration attribute for the required role. But
  144. what
  145. about:<programlisting> @PreAuthorize("hasPermission(#contact, 'admin')")
  146. public void deletePermission(Contact contact, Sid recipient, Permission permission);</programlisting>Here
  147. we're actually using a method argument as part of the expression to decide
  148. whether the current user has the <quote>admin</quote>permission for the given
  149. contact. The built-in <literal>hasPermission()</literal> expression is linked
  150. into the Spring Security ACL module through the application context, as we'll
  151. <link xlink:href="#el-permission-evaluator">see below</link>. You can access
  152. any of the method arguments by name as expression variables, provided your code
  153. has debug information compiled in. Any Spring-EL functionality is available
  154. within the expression, so you can also access properties on the arguments. For
  155. example, if you wanted a particular method to only allow access to a user whose
  156. username matched that of the contact, you could write</para>
  157. <programlisting> @PreAuthorize("#contact.name == principal.name)")
  158. public void doSomething(Contact contact);</programlisting>
  159. <para>Here we are accessing another built–in expression, which is the
  160. <literal>principal</literal> of the current Spring Security
  161. <interfacename>Authentication</interfacename> object obtained from the
  162. security context. You can also access the
  163. <interfacename>Authentication</interfacename> object itself directly using
  164. the expression name <literal>authentication</literal>.</para>
  165. <para>Less commonly, you may wish to perform an access-control check after the
  166. method has been invoked. This can be achieved using the
  167. <literal>@PostAuthorize</literal> annotation. To access the return value
  168. from a method, use the built–in name <literal>returnObject</literal> in the
  169. expression.</para>
  170. </section>
  171. <section>
  172. <title>Filtering using <literal>@PreFilter</literal> and
  173. <literal>@PostFilter</literal></title>
  174. <para>As you may already be aware, Spring Security supports filtering of collections
  175. and arrays and this can now be achieved using expressions. This is most commonly
  176. performed on the return value of a method. For
  177. example:<programlisting> @PreAuthorize("hasRole('ROLE_USER')")
  178. @PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
  179. public List&lt;Contact> getAll();</programlisting>When
  180. using the <literal>@PostFilter</literal> annotation, Spring Security iterates
  181. through the returned collection and removes any elements for which the supplied
  182. expression is false. The name <literal>filterObject</literal> refers to the
  183. current object in the collection. You can also filter before the method call,
  184. using <literal>@PreFilter</literal>, though this is a less common requirement.
  185. The syntax is just the same, but if there is more than one argument which is a
  186. collection type then you have to select one by name using the
  187. <literal>filterTarget</literal> property of this annotation.</para>
  188. <para>Note that filtering is obviously not a substitute for tuning your data
  189. retrieval queries. If you are filtering large collections and removing many of
  190. the entries then this is likely to be inefficient.</para>
  191. </section>
  192. </section>
  193. <section xml:id="el-method-built-in">
  194. <title>Built-In Expressions</title>
  195. <para>There are some built-in expressions which are specific to method security, which
  196. we have already seen in use above. The <literal>filterTarget</literal> and
  197. <literal>returnValue</literal> values are simple enough, but the use of the
  198. <literal>hasPermission()</literal> expression warrants a closer look.</para>
  199. <section xml:id="el-permission-evaluator">
  200. <title>The <interfacename>PermissionEvaluator</interfacename> interface</title>
  201. <para><literal>hasPermission()</literal> expressions are delegated to an instance of
  202. <interfacename>PermissionEvaluator</interfacename>. It is intended to bridge
  203. between the expression system and Spring Security's ACL system, allowing you to
  204. specify authorization constraints on domain objects, based on abstract
  205. permissions. It has no explicit dependencies on the ACL module, so you could
  206. swap that out for an alternative implementation if required. The interface has
  207. two methods:
  208. <programlisting language="java"> boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission);
  209. boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);</programlisting>which
  210. map directly to the available versions of the expression, with the exception
  211. that the first argument (the <interfacename>Authentication</interfacename>
  212. object) is not supplied. The first is used in situations where the domain
  213. object, to which access is being controlled, is already loaded. Then expression
  214. will return true if the current user has the given permission for that object.
  215. The second version is used in cases where the object is not loaded, but its
  216. identifier is known. An abstract <quote>type</quote> specifier for the domain
  217. object is also required, allowing the correct ACL permissions to be loaded. This
  218. has traditionally been the Java class of the object, but does not have to be as
  219. long as it is consistent with how the permissions are loaded.</para>
  220. <para>To use <literal>hasPermission()</literal> expressions, you have to explicitly
  221. configure a <interfacename>PermissionEvaluator</interfacename> in your
  222. application context. This would look something like this:<programlisting language="xml"> <![CDATA[ <security:global-method-security pre-post-annotations="enabled">
  223. <security:expression-handler ref="expressionHandler"/>
  224. </security:global-method-security>
  225. <bean id="expressionHandler"
  226. class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
  227. <property name="permissionEvaluator" ref="myPermissionEvaluator"/>
  228. </bean>]]></programlisting>Where <literal>myPermissionEvaluator</literal> is the bean which
  229. implements <interfacename>PermissionEvaluator</interfacename>. Usually this will
  230. be the implementation from the ACL module which is called
  231. <classname>AclPermissionEvaluator</classname>. See the
  232. <quote>Contacts</quote> sample application configuration for more
  233. details.</para>
  234. </section>
  235. </section>
  236. </section>
  237. </chapter>