el-access.xml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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>
  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 anonyous or
  83. 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>
  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. You can
  151. access any of the method arguments by name as expression variables, provided
  152. your code has debug information compiled in. Any Spring-EL functionality is
  153. available within the expression, so you can also access properties on the
  154. arguments. For example, if you wanted a particular method to only allow access
  155. to a user whose username matched that of the contact, you could write</para>
  156. <programlisting> @PreAuthorize("#contact.name == principal.name)")
  157. public void doSomething(Contact contact);</programlisting>
  158. <para>Here we are accessing another built–in expression, which is the
  159. <literal>principal</literal> of the current Spring Security
  160. <interfacename>Authentication</interfacename> object obtained from the
  161. security context. You can also access the
  162. <interfacename>Authentication</interfacename> object itself directly using
  163. the expression name <literal>authentication</literal>.</para>
  164. <para>Less commonly, you may wish to perform an access-control check after the
  165. method has been invoked. This can be achieved using the
  166. <literal>@PostAuthorize</literal> annotation. To access the return value
  167. from a method, use the built–in name <literal>returnObject</literal> in the
  168. expression.</para>
  169. </section>
  170. <section>
  171. <title>Filtering using <literal>@PreFilter</literal> and
  172. <literal>@PostFilter</literal></title>
  173. <para>As you may already be aware, Spring Security supports filtering of collections
  174. and arrays and this can now be achieved using expressions. This is most commonly
  175. performed on the return value of a method. For
  176. example:<programlisting> @PreAuthorize("hasRole('ROLE_USER')")
  177. @PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
  178. public List&lt;Contact> getAll();</programlisting>When
  179. using the <literal>@PostFilter</literal> annotation, Spring Security iterates
  180. through the returned collection and removes any elements for which the supplied
  181. expression is false. The name <literal>filterObject</literal> refers to the
  182. current object in the collection. You can also filter before the method call,
  183. using <literal>@PreFilter</literal>, though this is a less common requirement.
  184. The syntax is just the same, but if there is more than one argument which is a
  185. collection type then you have to select one by name using the
  186. <literal>filterTarget</literal> property of this annotation.</para>
  187. <para>Note that filtering is obviously not a substitute for tuning your data
  188. retrieval queries. If you are filtering large collections and removing many of
  189. the entries then this is likely to be inefficient.</para>
  190. </section>
  191. </section>
  192. </section>
  193. </chapter>