authorization-common.xml 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="authz-arch"
  2. xmlns:xlink="http://www.w3.org/1999/xlink">
  3. <info>
  4. <title>Authorization Architecture</title>
  5. </info>
  6. <section xml:id="authz-authorities">
  7. <info>
  8. <title>Authorities</title>
  9. </info>
  10. <para>As we saw in the <link xlink:href="#tech-granted-authority">technical overview</link>, all
  11. <interfacename>Authentication</interfacename> implementations store a list of
  12. <interfacename>GrantedAuthority</interfacename> objects. These represent the authorities
  13. that have been granted to the principal. The <interfacename>GrantedAuthority</interfacename>
  14. objects are inserted into the <interfacename>Authentication</interfacename> object by the
  15. <interfacename>AuthenticationManager</interfacename> and are later read by
  16. <interfacename>AccessDecisionManager</interfacename>s when making authorization
  17. decisions.</para>
  18. <para><interfacename>GrantedAuthority</interfacename> is an interface with only one method:
  19. <programlisting>
  20. String getAuthority();
  21. </programlisting> This method allows
  22. <interfacename>AccessDecisionManager</interfacename>s to obtain a precise
  23. <literal>String</literal> representation of the
  24. <interfacename>GrantedAuthority</interfacename>. By returning a representation as a
  25. <literal>String</literal>, a <interfacename>GrantedAuthority</interfacename> can be easily
  26. <quote>read</quote> by most <interfacename>AccessDecisionManager</interfacename>s. If a
  27. <interfacename>GrantedAuthority</interfacename> cannot be precisely represented as a
  28. <literal>String</literal>, the <interfacename>GrantedAuthority</interfacename> is considered
  29. <quote>complex</quote> and <literal>getAuthority()</literal> must return
  30. <literal>null</literal>.</para>
  31. <para>An example of a <quote>complex</quote>
  32. <interfacename>GrantedAuthority</interfacename> would be an implementation that stores a list
  33. of operations and authority thresholds that apply to different customer account numbers.
  34. Representing this complex <interfacename>GrantedAuthority</interfacename> as a
  35. <literal>String</literal> would be quite difficult, and as a result the
  36. <literal>getAuthority()</literal> method should return <literal>null</literal>. This will
  37. indicate to any <interfacename>AccessDecisionManager</interfacename> that it will need to
  38. specifically support the <interfacename>GrantedAuthority</interfacename> implementation in
  39. order to understand its contents.</para>
  40. <para>Spring Security includes one concrete <interfacename>GrantedAuthority</interfacename>
  41. implementation, <literal>GrantedAuthorityImpl</literal>. This allows any user-specified
  42. <literal>String</literal> to be converted into a
  43. <interfacename>GrantedAuthority</interfacename>. All
  44. <classname>AuthenticationProvider</classname>s included with the security architecture use
  45. <literal>GrantedAuthorityImpl</literal> to populate the
  46. <interfacename>Authentication</interfacename> object.</para>
  47. </section>
  48. <section xml:id="authz-pre-invocation">
  49. <info>
  50. <title>Pre-Invocation Handling</title>
  51. </info>
  52. <para> As we've also seen in the <link xlink:href="#secure-objects">Technical Overview</link>
  53. chapter, Spring Security provides interceptors which control access to secure objects such as
  54. method invocations or web requests. A pre-invocation decision on whether the invocation is
  55. allowed to proceed is made by the <interfacename>AccessDecisionManager</interfacename>. </para>
  56. <section xml:id="authz-access-decision-manager">
  57. <title>The AccessDecisionManager</title>
  58. <para>The <interfacename>AccessDecisionManager</interfacename> is called by the
  59. <classname>AbstractSecurityInterceptor</classname> and is responsible for making final
  60. access control decisions. The <interfacename>AccessDecisionManager</interfacename> interface
  61. contains three methods:
  62. <programlisting>
  63. void decide(Authentication authentication, Object secureObject,
  64. List&lt;ConfigAttribute&gt; config) throws AccessDeniedException;
  65. boolean supports(ConfigAttribute attribute);
  66. boolean supports(Class clazz);
  67. </programlisting>
  68. The <interfacename>AccessDecisionManager</interfacename>'s <methodname>decide</methodname>
  69. method is passed all the relevant information it needs in order to make an authorization
  70. decision. In particular, passing the secure <literal>Object</literal> enables those
  71. arguments contained in the actual secure object invocation to be inspected. For example,
  72. let's assume the secure object was a <classname>MethodInvocation</classname>. It would be
  73. easy to query the <classname>MethodInvocation</classname> for any
  74. <literal>Customer</literal> argument, and then implement some sort of security logic in
  75. the <interfacename>AccessDecisionManager</interfacename> to ensure the principal is
  76. permitted to operate on that customer. Implementations are expected to throw an
  77. <literal>AccessDeniedException</literal> if access is denied.</para>
  78. <para>The <literal>supports(ConfigAttribute)</literal> method is called by the
  79. <classname>AbstractSecurityInterceptor</classname> at startup time to determine if the
  80. <interfacename>AccessDecisionManager</interfacename> can process the passed
  81. <literal>ConfigAttribute</literal>. The <literal>supports(Class)</literal> method is
  82. called by a security interceptor implementation to ensure the configured
  83. <interfacename>AccessDecisionManager</interfacename> supports the type of secure object
  84. that the security interceptor will present.</para>
  85. </section>
  86. <section xml:id="authz-voting-based">
  87. <title>Voting-Based AccessDecisionManager Implementations</title>
  88. <para>Whilst users can implement their own
  89. <interfacename>AccessDecisionManager</interfacename> to control all aspects of
  90. authorization, Spring Security includes several
  91. <interfacename>AccessDecisionManager</interfacename> implementations that are based on
  92. voting. <xref linkend="authz-access-voting"/> illustrates the relevant classes.</para>
  93. <figure xml:id="authz-access-voting">
  94. <title>Voting Decision Manager</title>
  95. <mediaobject>
  96. <!--
  97. <imageobject role="fo">
  98. <imagedata align="center" fileref="resources/images/AccessDecisionVoting.gif" format="GIF"/>
  99. </imageobject>
  100. -->
  101. <imageobject>
  102. <imagedata align="center" scalefit="1" fileref="images/AccessDecisionVoting.gif"
  103. format="GIF" />
  104. </imageobject>
  105. </mediaobject>
  106. </figure>
  107. <para>Using this approach, a series of <interfacename>AccessDecisionVoter</interfacename>
  108. implementations are polled on an authorization decision. The
  109. <interfacename>AccessDecisionManager</interfacename> then decides whether or not to throw
  110. an <literal>AccessDeniedException</literal> based on its assessment of the votes.</para>
  111. <para>The <interfacename>AccessDecisionVoter</interfacename> interface has three methods:
  112. <programlisting>
  113. int vote(Authentication authentication, Object object, List&lt;ConfigAttribute&gt; config);
  114. boolean supports(ConfigAttribute attribute);
  115. boolean supports(Class clazz);
  116. </programlisting>
  117. Concrete implementations return an <literal>int</literal>, with possible values being
  118. reflected in the <interfacename>AccessDecisionVoter</interfacename> static fields
  119. <literal>ACCESS_ABSTAIN</literal>, <literal>ACCESS_DENIED</literal> and
  120. <literal>ACCESS_GRANTED</literal>. A voting implementation will return
  121. <literal>ACCESS_ABSTAIN</literal> if it has no opinion on an authorization decision. If it
  122. does have an opinion, it must return either <literal>ACCESS_DENIED</literal> or
  123. <literal>ACCESS_GRANTED</literal>.</para>
  124. <para>There are three concrete <interfacename>AccessDecisionManager</interfacename>s provided
  125. with Spring Security that tally the votes. The <literal>ConsensusBased</literal>
  126. implementation will grant or deny access based on the consensus of non-abstain votes.
  127. Properties are provided to control behavior in the event of an equality of votes or if all
  128. votes are abstain. The <literal>AffirmativeBased</literal> implementation will grant access
  129. if one or more <literal>ACCESS_GRANTED</literal> votes were received (i.e. a deny vote will
  130. be ignored, provided there was at least one grant vote). Like the
  131. <literal>ConsensusBased</literal> implementation, there is a parameter that controls the
  132. behavior if all voters abstain. The <literal>UnanimousBased</literal> provider expects
  133. unanimous <literal>ACCESS_GRANTED</literal> votes in order to grant access, ignoring
  134. abstains. It will deny access if there is any <literal>ACCESS_DENIED</literal> vote. Like
  135. the other implementations, there is a parameter that controls the behaviour if all voters
  136. abstain.</para>
  137. <para>It is possible to implement a custom
  138. <interfacename>AccessDecisionManager</interfacename> that tallies votes differently. For
  139. example, votes from a particular <interfacename>AccessDecisionVoter</interfacename> might
  140. receive additional weighting, whilst a deny vote from a particular voter may have a veto
  141. effect.</para>
  142. <section xml:id="authz-role-voter">
  143. <title><classname>RoleVoter</classname></title>
  144. <para> The most commonly used <interfacename>AccessDecisionVoter</interfacename> provided
  145. with Spring Security is the simple <classname>RoleVoter</classname>, which treats
  146. configuration attributes as simple role names and votes to grant access if the user has
  147. been assigned that role.</para>
  148. <para>It will vote if any <interfacename>ConfigAttribute</interfacename> begins with the
  149. prefix <literal>ROLE_</literal>. It will vote to grant access if there is a
  150. <interfacename>GrantedAuthority</interfacename> which returns a
  151. <literal>String</literal> representation (via the <literal>getAuthority()</literal>
  152. method) exactly equal to one or more <literal>ConfigAttributes</literal> starting with
  153. <literal>ROLE_</literal>. If there is no exact match of any
  154. <literal>ConfigAttribute</literal> starting with <literal>ROLE_</literal>, the
  155. <literal>RoleVoter</literal> will vote to deny access. If no
  156. <literal>ConfigAttribute</literal> begins with <literal>ROLE_</literal>, the voter will
  157. abstain.</para>
  158. </section>
  159. <section xml:id="authz-authenticated-voter">
  160. <title><classname>AuthenticatedVoter</classname></title>
  161. <para> Another voter which we've implicitly seen is the
  162. <classname>AuthenticatedVoter</classname>, which can be used to differentiate between
  163. anonymous, fully-authenticated and remember-me authenticated users. When we've used the
  164. attribute <literal>IS_AUTHENTICATED_ANONYMOUSLY</literal> to grant anonymous access, this
  165. attribute was being processed by the <classname>AuthenticatedVoter</classname>. See the
  166. Javadoc for this class for more information. </para>
  167. </section>
  168. <section>
  169. <title>Custom Voters</title>
  170. <para>It is also possible to implement a custom
  171. <interfacename>AccessDecisionVoter</interfacename>. Several examples are provided in
  172. Spring Security unit tests, including <literal>ContactSecurityVoter</literal> and
  173. <literal>DenyVoter</literal>. The <literal>ContactSecurityVoter</literal> abstains from
  174. voting decisions where a <literal>CONTACT_OWNED_BY_CURRENT_USER</literal>
  175. <literal>ConfigAttribute</literal> is not found. If voting, it queries the
  176. <classname>MethodInvocation</classname> to extract the owner of the
  177. <literal>Contact</literal> object that is subject of the method call. It votes to grant
  178. access if the <literal>Contact</literal> owner matches the principal presented in the
  179. <interfacename>Authentication</interfacename> object. It could have just as easily
  180. compared the <literal>Contact</literal> owner with some
  181. <interfacename>GrantedAuthority</interfacename> the
  182. <interfacename>Authentication</interfacename> object presented. All of this is achieved
  183. with relatively few lines of code and demonstrates the flexibility of the authorization
  184. model.</para>
  185. </section>
  186. </section>
  187. </section>
  188. <section xml:id="authz-after-invocation-handling">
  189. <info>
  190. <title>After Invocation Handling</title>
  191. </info>
  192. <para>Whilst the <interfacename>AccessDecisionManager</interfacename> is called by the
  193. <classname>AbstractSecurityInterceptor</classname> before proceeding with the secure object
  194. invocation, some applications need a way of modifying the object actually returned by the
  195. secure object invocation. Whilst you could easily implement your own AOP concern to achieve
  196. this, Spring Security provides a convenient hook that has several concrete implementations
  197. that integrate with its ACL capabilities.</para>
  198. <para><xref linkend="authz-after-invocation"/> illustrates Spring Security's
  199. <literal>AfterInvocationManager</literal> and its concrete implementations. <figure
  200. xml:id="authz-after-invocation">
  201. <title>After Invocation Implementation</title>
  202. <mediaobject>
  203. <imageobject>
  204. <imagedata align="center" scalefit="1" fileref="images/AfterInvocation.gif" format="GIF"
  205. />
  206. </imageobject>
  207. </mediaobject>
  208. </figure></para>
  209. <para>Like many other parts of Spring Security, <literal>AfterInvocationManager</literal> has a
  210. single concrete implementation, <literal>AfterInvocationProviderManager</literal>, which polls
  211. a list of <literal>AfterInvocationProvider</literal>s. Each
  212. <literal>AfterInvocationProvider</literal> is allowed to modify the return object or throw
  213. an <literal>AccessDeniedException</literal>. Indeed multiple providers can modify the object,
  214. as the result of the previous provider is passed to the next in the list.</para>
  215. <para>Please be aware that if you're using <literal>AfterInvocationManager</literal>, you will
  216. still need configuration attributes that allow the
  217. <classname>MethodSecurityInterceptor</classname>'s
  218. <interfacename>AccessDecisionManager</interfacename> to allow an operation. If you're using
  219. the typical Spring Security included <interfacename>AccessDecisionManager</interfacename>
  220. implementations, having no configuration attributes defined for a particular secure method
  221. invocation will cause each <interfacename>AccessDecisionVoter</interfacename> to abstain from
  222. voting. In turn, if the <interfacename>AccessDecisionManager</interfacename> property
  223. "<literal>allowIfAllAbstainDecisions</literal>" is <literal>false</literal>, an
  224. <literal>AccessDeniedException</literal> will be thrown. You may avoid this potential issue
  225. by either (i) setting "<literal>allowIfAllAbstainDecisions</literal>" to
  226. <literal>true</literal> (although this is generally not recommended) or (ii) simply ensure
  227. that there is at least one configuration attribute that an
  228. <interfacename>AccessDecisionVoter</interfacename> will vote to grant access for. This
  229. latter (recommended) approach is usually achieved through a <literal>ROLE_USER</literal> or
  230. <literal>ROLE_AUTHENTICATED</literal> configuration attribute.</para>
  231. <!-- TODO: Move to ACL section and add reference here -->
  232. <!--
  233. <section xml:id="after-invocation-acl-aware">
  234. <info>
  235. <title>ACL-Aware AfterInvocationProviders</title>
  236. </info>
  237. <para>A common services layer method we've all written at one stage or another looks like
  238. this:</para>
  239. <para>
  240. <programlisting>public Contact getById(Integer id);</programlisting>
  241. </para>
  242. <para>Quite often, only principals with permission to read the <literal>Contact</literal>
  243. should be allowed to obtain it. In this situation the
  244. <interfacename>AccessDecisionManager</interfacename> approach provided by the
  245. <classname>AbstractSecurityInterceptor</classname> will not suffice. This is because the
  246. identity of the <literal>Contact</literal> is all that is available before the secure object
  247. is invoked. The <classname>AclEntryAfterInvocationProvider</classname> delivers a solution,
  248. and is configured as follows: <programlisting><![CDATA[
  249. <bean id="afterAclRead"
  250. class="org.springframework.security.acls.afterinvocation.AclEntryAfterInvocationProvider">
  251. <constructor-arg ref="aclService"/>
  252. <constructor-arg>
  253. <list>
  254. <ref local="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
  255. <ref local="org.springframework.security.acls.domain.BasePermission.READ"/>
  256. </list>
  257. </constructor-arg>
  258. </bean>
  259. ]]></programlisting> In the above example, the <literal>Contact</literal> will be retrieved and
  260. passed to the <classname>AclEntryAfterInvocationProvider</classname>. The provider will
  261. thrown an <classname>AccessDeniedException</classname> if one of the listed
  262. <literal>requirePermission</literal>s is not held by the
  263. <interfacename>Authentication</interfacename>. The
  264. <classname>AclEntryAfterInvocationProvider</classname> queries the acl service to
  265. determine the ACL that applies for this domain object to this
  266. <interfacename>Authentication</interfacename>.</para>
  267. <para>Similar to the <classname>AclEntryAfterInvocationProvider</classname> is
  268. <classname>AclEntryAfterInvocationCollectionFilteringProvider</classname>. It is designed
  269. to remove <literal>Collection</literal> or array elements for which a principal does not
  270. have access. It never thrown an <classname>AccessDeniedException</classname> - simply
  271. silently removes the offending elements. The provider is configured as follows: <programlisting><![CDATA[
  272. <bean id="afterAclCollectionRead"
  273. class="org.springframework.security.acls.afterinvocation.AclEntryAfterInvocationCollectionFilteringProvider">
  274. <constructor-arg ref="aclService"/>
  275. <constructor-arg>
  276. <list>
  277. <ref local="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
  278. <ref local="org.springframework.security.acls.domain.BasePermission.READ"/>
  279. </list>
  280. </constructor-arg>
  281. </bean>
  282. ]]> </programlisting> As you can imagine, the returned <literal>Object</literal> must be a
  283. <literal>Collection</literal> or array for this provider to operate. It will remove any
  284. element if the <literal>AclManager</literal> indicates the
  285. <interfacename>Authentication</interfacename> does not hold one of the listed
  286. <literal>requirePermission</literal>s.</para>
  287. <para>The Contacts sample application demonstrates these two
  288. <literal>AfterInvocationProvider</literal>s.</para>
  289. </section> -->
  290. </section>
  291. <!-- TODO: Move taglibs to a separate chapter which describes them all
  292. <section xml:id="authorization-taglibs">
  293. <info>
  294. <title>Authorization Tag Libraries</title>
  295. </info>
  296. <para><literal>AuthorizeTag</literal> is used to include content if the current principal holds
  297. certain <interfacename>GrantedAuthority</interfacename>s.</para>
  298. <para>The following JSP fragment illustrates how to use the
  299. <literal>AuthorizeTag</literal>:</para>
  300. <para>
  301. <programlisting>
  302. <![CDATA[
  303. <security:authorize ifAllGranted="ROLE_SUPERVISOR">
  304. <td>
  305. <a href="del.htm?id=<c:out value="${contact.id}"/>">Del</a>
  306. </td>
  307. </security:authorize>
  308. ]]></programlisting>
  309. </para>
  310. <para>This tag would cause the tag's body to be output if the principal has been granted
  311. ROLE_SUPERVISOR.</para>
  312. <para>The <literal>security:authorize</literal> tag declares the following attributes:</para>
  313. <para>
  314. <itemizedlist spacing="compact">
  315. <listitem>
  316. <para><literal>ifAllGranted</literal>: All the listed roles must be granted for the tag to
  317. output its body.</para>
  318. </listitem>
  319. <listitem>
  320. <para><literal>ifAnyGranted</literal>: Any of the listed roles must be granted for the tag
  321. to output its body.</para>
  322. </listitem>
  323. <listitem>
  324. <para><literal>ifNotGranted</literal>: None of the listed roles must be granted for the
  325. tag to output its body.</para>
  326. </listitem>
  327. </itemizedlist>
  328. </para>
  329. <para>You'll note that in each attribute you can list multiple roles. Simply separate the roles
  330. using a comma. The <literal>authorize</literal> tag ignores whitespace in attributes.</para>
  331. <para>The tag library logically ANDs all of it's parameters together. This means that if you
  332. combine two or more attributes, all attributes must be true for the tag to output it's body.
  333. Don't add an <literal>ifAllGranted="ROLE_SUPERVISOR"</literal>, followed by an
  334. <literal>ifNotGranted="ROLE_SUPERVISOR"</literal>, or you'll be surprised to never see the
  335. tag's body.</para>
  336. <para>By requiring all attributes to return true, the authorize tag allows you to create more
  337. complex authorization scenarios. For example, you could declare an
  338. <literal>ifAllGranted="ROLE_SUPERVISOR"</literal> and an
  339. <literal>ifNotGranted="ROLE_NEWBIE_SUPERVISOR"</literal> in the same tag, in order to
  340. prevent new supervisors from seeing the tag body. However it would no doubt be simpler to use
  341. <literal>ifAllGranted="ROLE_EXPERIENCED_SUPERVISOR"</literal> rather than inserting NOT
  342. conditions into your design.</para>
  343. <para>One last item: the tag verifies the authorizations in a specific order: first
  344. <literal>ifNotGranted</literal>, then <literal>ifAllGranted</literal>, and finally,
  345. <literal>if AnyGranted</literal>.</para>
  346. <para><literal>AccessControlListTag</literal> is used to include content if the current
  347. principal has an ACL to the indicated domain object.</para>
  348. <para>The following JSP fragment illustrates how to use the
  349. <literal>AccessControlListTag</literal>: <programlisting><![CDATA[
  350. <security:accesscontrollist domainObject="${contact}" hasPermission="8,16">
  351. <td><a href="<c:url value="del.htm"><c:param name="contactId" value="${contact.id}"/></c:url>">Del</a></td>
  352. </security:accesscontrollist>
  353. ]]></programlisting> This tag would cause the tag's body to be output if the principal holds either
  354. permission 16 or permission 1 for the "contact" domain object. The numbers are actually
  355. integers that are used with <literal>BasePermission</literal> bit masking. Please refer to the
  356. ACL section of this reference guide to understand more about the ACL capabilities of Spring
  357. Security.</para>
  358. <para><literal>AclTag</literal> is part of the old ACL module and should be considered
  359. deprecated. For the sake of historical reference, works exactly the samae as
  360. <literal>AccessControlListTag</literal>.</para>
  361. </section> -->
  362. </chapter>