el-access.xml 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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 expressions
  22. 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 from
  55. 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> element
  94. 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 as
  110. 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>
  140. @PreAuthorize("hasRole('ROLE_USER')")
  141. public void create(Contact contact);</programlisting>which
  142. means that access will only be allowed for users with the role "ROLE_USER".
  143. Obviously the same thing could easily be achieved using a traditional
  144. configuration and a simple configuration attribute for the required role. But
  145. what
  146. about:<programlisting>
  147. @PreAuthorize("hasPermission(#contact, 'admin')")
  148. public void deletePermission(Contact contact, Sid recipient, Permission permission);</programlisting>Here
  149. we're actually using a method argument as part of the expression to decide
  150. whether the current user has the <quote>admin</quote>permission for the given
  151. contact. The built-in <literal>hasPermission()</literal> expression is linked
  152. into the Spring Security ACL module through the application context, as we'll
  153. <link xlink:href="#el-permission-evaluator">see below</link>. You can access any
  154. of the method arguments by name as expression variables, provided your code has
  155. debug information compiled in. Any Spring-EL functionality is available within
  156. the expression, so you can also access properties on the arguments. For example,
  157. if you wanted a particular method to only allow access to a user whose username
  158. matched that of the contact, you could write</para>
  159. <programlisting>
  160. @PreAuthorize("#contact.name == principal.name)")
  161. public void doSomething(Contact contact);</programlisting>
  162. <para>Here we are accessing another built–in expression, which is the
  163. <literal>principal</literal> of the current Spring Security
  164. <interfacename>Authentication</interfacename> object obtained from the security
  165. context. You can also access the <interfacename>Authentication</interfacename>
  166. object itself directly using the expression name
  167. <literal>authentication</literal>.</para>
  168. <para>Less commonly, you may wish to perform an access-control check after the
  169. method has been invoked. This can be achieved using the
  170. <literal>@PostAuthorize</literal> annotation. To access the return value from a
  171. method, use the built–in name <literal>returnObject</literal> in the
  172. expression.</para>
  173. </section>
  174. <section>
  175. <title>Filtering using <literal>@PreFilter</literal> and
  176. <literal>@PostFilter</literal></title>
  177. <para>As you may already be aware, Spring Security supports filtering of collections
  178. and arrays and this can now be achieved using expressions. This is most commonly
  179. performed on the return value of a method. For
  180. example:<programlisting> @PreAuthorize("hasRole('ROLE_USER')")
  181. @PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
  182. public List&lt;Contact> getAll();</programlisting>When
  183. using the <literal>@PostFilter</literal> annotation, Spring Security iterates
  184. through the returned collection and removes any elements for which the supplied
  185. expression is false. The name <literal>filterObject</literal> refers to the
  186. current object in the collection. You can also filter before the method call,
  187. using <literal>@PreFilter</literal>, though this is a less common requirement.
  188. The syntax is just the same, but if there is more than one argument which is a
  189. collection type then you have to select one by name using the
  190. <literal>filterTarget</literal> property of this annotation.</para>
  191. <para>Note that filtering is obviously not a substitute for tuning your data
  192. retrieval queries. If you are filtering large collections and removing many of
  193. the entries then this is likely to be inefficient.</para>
  194. </section>
  195. </section>
  196. <section xml:id="el-method-built-in">
  197. <title>Built-In Expressions</title>
  198. <para>There are some built-in expressions which are specific to method security, which
  199. we have already seen in use above. The <literal>filterTarget</literal> and
  200. <literal>returnValue</literal> values are simple enough, but the use of the
  201. <literal>hasPermission()</literal> expression warrants a closer look.</para>
  202. <section xml:id="el-permission-evaluator">
  203. <title>The <interfacename>PermissionEvaluator</interfacename> interface</title>
  204. <para><literal>hasPermission()</literal> expressions are delegated to an instance of
  205. <interfacename>PermissionEvaluator</interfacename>. It is intended to bridge
  206. between the expression system and Spring Security's ACL system, allowing you to
  207. specify authorization constraints on domain objects, based on abstract
  208. permissions. It has no explicit dependencies on the ACL module, so you could
  209. swap that out for an alternative implementation if required. The interface has
  210. two methods:
  211. <programlisting language="java">
  212. boolean hasPermission(Authentication authentication, Object targetDomainObject,
  213. Object permission);
  214. boolean hasPermission(Authentication authentication, Serializable targetId,
  215. String targetType, Object permission);
  216. </programlisting>which
  217. map directly to the available versions of the expression, with the exception
  218. that the first argument (the <interfacename>Authentication</interfacename>
  219. object) is not supplied. The first is used in situations where the domain
  220. object, to which access is being controlled, is already loaded. Then expression
  221. will return true if the current user has the given permission for that object.
  222. The second version is used in cases where the object is not loaded, but its
  223. identifier is known. An abstract <quote>type</quote> specifier for the domain
  224. object is also required, allowing the correct ACL permissions to be loaded. This
  225. has traditionally been the Java class of the object, but does not have to be as
  226. long as it is consistent with how the permissions are loaded.</para>
  227. <para>To use <literal>hasPermission()</literal> expressions, you have to explicitly
  228. configure a <interfacename>PermissionEvaluator</interfacename> in your
  229. application context. This would look something like this: <programlisting language="xml"> <![CDATA[
  230. <security:global-method-security pre-post-annotations="enabled">
  231. <security:expression-handler ref="expressionHandler"/>
  232. </security:global-method-security>
  233. <bean id="expressionHandler" class=
  234. "org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
  235. <property name="permissionEvaluator" ref="myPermissionEvaluator"/>
  236. </bean>]]></programlisting>Where <literal>myPermissionEvaluator</literal> is the bean which
  237. implements <interfacename>PermissionEvaluator</interfacename>. Usually this will
  238. be the implementation from the ACL module which is called
  239. <classname>AclPermissionEvaluator</classname>. See the <quote>Contacts</quote>
  240. sample application configuration for more details.</para>
  241. </section>
  242. </section>
  243. </section>
  244. </chapter>