authorization-common.xml 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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 linkend="tech-granted-authority">technical overview</link>,
  11. all <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
  14. <interfacename>GrantedAuthority</interfacename> objects are inserted into the
  15. <interfacename>Authentication</interfacename> object by the
  16. <interfacename>AuthenticationManager</interfacename> and are later read by
  17. <interfacename>AccessDecisionManager</interfacename>s when making authorization
  18. decisions.</para>
  19. <para><interfacename>GrantedAuthority</interfacename> is an interface with only one method:
  20. <programlisting language="java">
  21. String getAuthority();
  22. </programlisting> This method allows
  23. <interfacename>AccessDecisionManager</interfacename>s to obtain a precise
  24. <literal>String</literal> representation of the
  25. <interfacename>GrantedAuthority</interfacename>. By returning a representation as a
  26. <literal>String</literal>, a <interfacename>GrantedAuthority</interfacename> can be
  27. easily <quote>read</quote> by most
  28. <interfacename>AccessDecisionManager</interfacename>s. If a
  29. <interfacename>GrantedAuthority</interfacename> cannot be precisely represented as a
  30. <literal>String</literal>, the <interfacename>GrantedAuthority</interfacename> is
  31. considered <quote>complex</quote> and <literal>getAuthority()</literal> must return
  32. <literal>null</literal>.</para>
  33. <para>An example of a <quote>complex</quote> <interfacename>GrantedAuthority</interfacename>
  34. would be an implementation that stores a list of operations and authority thresholds
  35. that apply to different customer account numbers. Representing this complex
  36. <interfacename>GrantedAuthority</interfacename> as a <literal>String</literal> would be
  37. quite difficult, and as a result the <literal>getAuthority()</literal> method should
  38. return <literal>null</literal>. This will indicate to any
  39. <interfacename>AccessDecisionManager</interfacename> that it will need to specifically
  40. support the <interfacename>GrantedAuthority</interfacename> implementation in order to
  41. understand its contents.</para>
  42. <para>Spring Security includes one concrete <interfacename>GrantedAuthority</interfacename>
  43. implementation, <literal>GrantedAuthorityImpl</literal>. This allows any user-specified
  44. <literal>String</literal> to be converted into a
  45. <interfacename>GrantedAuthority</interfacename>. All
  46. <classname>AuthenticationProvider</classname>s included with the security architecture
  47. use <literal>GrantedAuthorityImpl</literal> to populate the
  48. <interfacename>Authentication</interfacename> object.</para>
  49. </section>
  50. <section xml:id="authz-pre-invocation">
  51. <info>
  52. <title>Pre-Invocation Handling</title>
  53. </info>
  54. <para> As we've also seen in the <link linkend="secure-objects">Technical
  55. Overview</link> chapter, Spring Security provides interceptors which control access to
  56. secure objects such as method invocations or web requests. A pre-invocation decision on
  57. whether the invocation is allowed to proceed is made by the
  58. <interfacename>AccessDecisionManager</interfacename>. </para>
  59. <section xml:id="authz-access-decision-manager">
  60. <title>The AccessDecisionManager</title>
  61. <para>The <interfacename>AccessDecisionManager</interfacename> is called by the
  62. <classname>AbstractSecurityInterceptor</classname> and is responsible for making
  63. final access control decisions. The
  64. <interfacename>AccessDecisionManager</interfacename> interface contains three
  65. methods:
  66. <programlisting language="java">
  67. void decide(Authentication authentication, Object secureObject,
  68. Collection&lt;ConfigAttribute&gt; attrs) throws AccessDeniedException;
  69. boolean supports(ConfigAttribute attribute);
  70. boolean supports(Class clazz);
  71. </programlisting>
  72. The <interfacename>AccessDecisionManager</interfacename>'s
  73. <methodname>decide</methodname> method is passed all the relevant information it
  74. needs in order to make an authorization decision. In particular, passing the secure
  75. <literal>Object</literal> enables those arguments contained in the actual secure
  76. object invocation to be inspected. For example, let's assume the secure object was a
  77. <classname>MethodInvocation</classname>. It would be easy to query the
  78. <classname>MethodInvocation</classname> for any <literal>Customer</literal>
  79. argument, and then implement some sort of security logic in the
  80. <interfacename>AccessDecisionManager</interfacename> to ensure the principal is
  81. permitted to operate on that customer. Implementations are expected to throw an
  82. <literal>AccessDeniedException</literal> if access is denied.</para>
  83. <para>The <literal>supports(ConfigAttribute)</literal> method is called by the
  84. <classname>AbstractSecurityInterceptor</classname> at startup time to determine if
  85. the <interfacename>AccessDecisionManager</interfacename> can process the passed
  86. <literal>ConfigAttribute</literal>. The <literal>supports(Class)</literal> method is
  87. called by a security interceptor implementation to ensure the configured
  88. <interfacename>AccessDecisionManager</interfacename> supports the type of secure
  89. object that the security interceptor will present.</para>
  90. </section>
  91. <section xml:id="authz-voting-based">
  92. <title>Voting-Based AccessDecisionManager Implementations</title>
  93. <para>Whilst users can implement their own
  94. <interfacename>AccessDecisionManager</interfacename> to control all aspects of
  95. authorization, Spring Security includes several
  96. <interfacename>AccessDecisionManager</interfacename> implementations that are based
  97. on voting. <xref linkend="authz-access-voting"/> illustrates the relevant
  98. classes.</para>
  99. <figure xml:id="authz-access-voting">
  100. <title>Voting Decision Manager</title>
  101. <mediaobject>
  102. <imageobject>
  103. <imagedata align="center" fileref="images/access-decision-voting.png"
  104. format="PNG" scale="75"/>
  105. </imageobject>
  106. </mediaobject>
  107. </figure>
  108. <para>Using this approach, a series of
  109. <interfacename>AccessDecisionVoter</interfacename> implementations are polled on an
  110. authorization decision. The <interfacename>AccessDecisionManager</interfacename>
  111. then decides whether or not to throw an <literal>AccessDeniedException</literal>
  112. based on its assessment of the votes.</para>
  113. <para>The <interfacename>AccessDecisionVoter</interfacename> interface has three
  114. methods:
  115. <programlisting language="java">
  116. int vote(Authentication authentication, Object object, Collection&lt;ConfigAttribute&gt; attrs);
  117. boolean supports(ConfigAttribute attribute);
  118. boolean supports(Class clazz);
  119. </programlisting>
  120. Concrete implementations return an <literal>int</literal>, with possible values
  121. being reflected in the <interfacename>AccessDecisionVoter</interfacename> static
  122. fields <literal>ACCESS_ABSTAIN</literal>, <literal>ACCESS_DENIED</literal> and
  123. <literal>ACCESS_GRANTED</literal>. A voting implementation will return
  124. <literal>ACCESS_ABSTAIN</literal> if it has no opinion on an authorization decision.
  125. If it does have an opinion, it must return either <literal>ACCESS_DENIED</literal>
  126. or <literal>ACCESS_GRANTED</literal>.</para>
  127. <para>There are three concrete <interfacename>AccessDecisionManager</interfacename>s
  128. provided with Spring Security that tally the votes. The
  129. <literal>ConsensusBased</literal> implementation will grant or deny access based on
  130. the consensus of non-abstain votes. Properties are provided to control behavior in
  131. the event of an equality of votes or if all votes are abstain. The
  132. <literal>AffirmativeBased</literal> implementation will grant access if one or more
  133. <literal>ACCESS_GRANTED</literal> votes were received (i.e. a deny vote will be
  134. ignored, provided there was at least one grant vote). Like the
  135. <literal>ConsensusBased</literal> implementation, there is a parameter that controls
  136. the behavior if all voters abstain. The <literal>UnanimousBased</literal> provider
  137. expects unanimous <literal>ACCESS_GRANTED</literal> votes in order to grant access,
  138. ignoring abstains. It will deny access if there is any
  139. <literal>ACCESS_DENIED</literal> vote. Like the other implementations, there is a
  140. parameter that controls the behaviour if all voters abstain.</para>
  141. <para>It is possible to implement a custom
  142. <interfacename>AccessDecisionManager</interfacename> that tallies votes differently.
  143. For example, votes from a particular
  144. <interfacename>AccessDecisionVoter</interfacename> might receive additional
  145. weighting, whilst a deny vote from a particular voter may have a veto effect.</para>
  146. <section xml:id="authz-role-voter">
  147. <title><classname>RoleVoter</classname></title>
  148. <para> The most commonly used <interfacename>AccessDecisionVoter</interfacename>
  149. provided with Spring Security is the simple <classname>RoleVoter</classname>,
  150. which treats configuration attributes as simple role names and votes to grant
  151. access if the user has been assigned that role.</para>
  152. <para>It will vote if any <interfacename>ConfigAttribute</interfacename> begins with
  153. the prefix <literal>ROLE_</literal>. It will vote to grant access if there is a
  154. <interfacename>GrantedAuthority</interfacename> which returns a
  155. <literal>String</literal> representation (via the
  156. <literal>getAuthority()</literal> method) exactly equal to one or more
  157. <literal>ConfigAttributes</literal> starting with the prefix
  158. <literal>ROLE_</literal>. If there is no exact match of any
  159. <literal>ConfigAttribute</literal> starting with <literal>ROLE_</literal>, the
  160. <literal>RoleVoter</literal> will vote to deny access. If no
  161. <literal>ConfigAttribute</literal> begins with <literal>ROLE_</literal>, the
  162. voter will abstain.</para>
  163. </section>
  164. <section xml:id="authz-authenticated-voter">
  165. <title><classname>AuthenticatedVoter</classname></title>
  166. <para> Another voter which we've implicitly seen is the
  167. <classname>AuthenticatedVoter</classname>, which can be used to differentiate
  168. between anonymous, fully-authenticated and remember-me authenticated users. Many
  169. sites allow certain limited access under remember-me authentication, but require
  170. a user to confirm their identity by logging in for full access.</para>
  171. <para>When we've used the attribute <literal>IS_AUTHENTICATED_ANONYMOUSLY</literal>
  172. to grant anonymous access, this attribute was being processed by the
  173. <classname>AuthenticatedVoter</classname>. See the Javadoc for this class for
  174. more information. </para>
  175. </section>
  176. <section xml:id="authz-custom-voter">
  177. <title>Custom Voters</title>
  178. <para>Obviously, you can also implement a custom
  179. <interfacename>AccessDecisionVoter</interfacename> and you can
  180. put just about any access-control logic you want in it. It might
  181. be specific to your application (business-logic related) or it
  182. might implement some security administration logic. For example, you'll find
  183. a <link xlink:href='http://blog.springsource.com/2009/01/02/spring-security-customization-part-2-adjusting-secured-session-in-real-time/'>
  184. blog article</link> on the SpringSource web site which describes how to
  185. use a voter to deny access in real-time to users whose accounts have
  186. been suspended.
  187. </para>
  188. </section>
  189. </section>
  190. </section>
  191. <section xml:id="authz-after-invocation-handling">
  192. <info>
  193. <title>After Invocation Handling</title>
  194. </info>
  195. <para>Whilst the <interfacename>AccessDecisionManager</interfacename> is called by the
  196. <classname>AbstractSecurityInterceptor</classname> before proceeding with the secure
  197. object invocation, some applications need a way of modifying the object actually
  198. returned by the secure object invocation. Whilst you could easily implement your own AOP
  199. concern to achieve this, Spring Security provides a convenient hook that has several
  200. concrete implementations that integrate with its ACL capabilities.</para>
  201. <para><xref linkend="authz-after-invocation"/> illustrates Spring Security's
  202. <literal>AfterInvocationManager</literal> and its concrete implementations. <figure
  203. xml:id="authz-after-invocation">
  204. <title>After Invocation Implementation</title>
  205. <mediaobject>
  206. <imageobject>
  207. <imagedata align="center" fileref="images/after-invocation.png" format="PNG"
  208. scale="75"/>
  209. </imageobject>
  210. </mediaobject>
  211. </figure></para>
  212. <para>Like many other parts of Spring Security, <literal>AfterInvocationManager</literal>
  213. has a single concrete implementation, <literal>AfterInvocationProviderManager</literal>,
  214. which polls a list of <literal>AfterInvocationProvider</literal>s. Each
  215. <literal>AfterInvocationProvider</literal> is allowed to modify the return object or
  216. throw an <literal>AccessDeniedException</literal>. Indeed multiple providers can modify
  217. the object, as the result of the previous provider is passed to the next in the
  218. list.</para>
  219. <para>Please be aware that if you're using <literal>AfterInvocationManager</literal>, you
  220. will still need configuration attributes that allow the
  221. <classname>MethodSecurityInterceptor</classname>'s
  222. <interfacename>AccessDecisionManager</interfacename> to allow an operation. If you're
  223. using the typical Spring Security included
  224. <interfacename>AccessDecisionManager</interfacename> implementations, having no
  225. configuration attributes defined for a particular secure method invocation will cause
  226. each <interfacename>AccessDecisionVoter</interfacename> to abstain from voting. In turn,
  227. if the <interfacename>AccessDecisionManager</interfacename> property
  228. "<literal>allowIfAllAbstainDecisions</literal>" is <literal>false</literal>, an
  229. <literal>AccessDeniedException</literal> will be thrown. You may avoid this potential
  230. issue by either (i) setting "<literal>allowIfAllAbstainDecisions</literal>" to
  231. <literal>true</literal> (although this is generally not recommended) or (ii) simply
  232. ensure that there is at least one configuration attribute that an
  233. <interfacename>AccessDecisionVoter</interfacename> will vote to grant access for. This
  234. latter (recommended) approach is usually achieved through a <literal>ROLE_USER</literal>
  235. or <literal>ROLE_AUTHENTICATED</literal> configuration attribute.</para>
  236. <!-- TODO: Move to ACL section and add reference here -->
  237. <!--
  238. <section xml:id="after-invocation-acl-aware">
  239. <info>
  240. <title>ACL-Aware AfterInvocationProviders</title>
  241. </info>
  242. <para>A common services layer method we've all written at one stage or another looks like
  243. this:</para>
  244. <para>
  245. <programlisting language="java">public Contact getById(Integer id);</programlisting>
  246. </para>
  247. <para>Quite often, only principals with permission to read the <literal>Contact</literal>
  248. should be allowed to obtain it. In this situation the
  249. <interfacename>AccessDecisionManager</interfacename> approach provided by the
  250. <classname>AbstractSecurityInterceptor</classname> will not suffice. This is because the
  251. identity of the <literal>Contact</literal> is all that is available before the secure object
  252. is invoked. The <classname>AclEntryAfterInvocationProvider</classname> delivers a solution,
  253. and is configured as follows: <programlisting language="xml"><![CDATA[
  254. <bean id="afterAclRead"
  255. class="org.springframework.security.acls.afterinvocation.AclEntryAfterInvocationProvider">
  256. <constructor-arg ref="aclService"/>
  257. <constructor-arg>
  258. <list>
  259. <ref local="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
  260. <ref local="org.springframework.security.acls.domain.BasePermission.READ"/>
  261. </list>
  262. </constructor-arg>
  263. </bean>
  264. ]]></programlisting> In the above example, the <literal>Contact</literal> will be retrieved and
  265. passed to the <classname>AclEntryAfterInvocationProvider</classname>. The provider will
  266. thrown an <classname>AccessDeniedException</classname> if one of the listed
  267. <literal>requirePermission</literal>s is not held by the
  268. <interfacename>Authentication</interfacename>. The
  269. <classname>AclEntryAfterInvocationProvider</classname> queries the acl service to
  270. determine the ACL that applies for this domain object to this
  271. <interfacename>Authentication</interfacename>.</para>
  272. <para>Similar to the <classname>AclEntryAfterInvocationProvider</classname> is
  273. <classname>AclEntryAfterInvocationCollectionFilteringProvider</classname>. It is designed
  274. to remove <literal>Collection</literal> or array elements for which a principal does not
  275. have access. It never thrown an <classname>AccessDeniedException</classname> - simply
  276. silently removes the offending elements. The provider is configured as follows: <programlisting language="xml"><![CDATA[
  277. <bean id="afterAclCollectionRead"
  278. class="org.springframework.security.acls.afterinvocation.AclEntryAfterInvocationCollectionFilteringProvider">
  279. <constructor-arg ref="aclService"/>
  280. <constructor-arg>
  281. <list>
  282. <ref local="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
  283. <ref local="org.springframework.security.acls.domain.BasePermission.READ"/>
  284. </list>
  285. </constructor-arg>
  286. </bean>
  287. ]]> </programlisting> As you can imagine, the returned <literal>Object</literal> must be a
  288. <literal>Collection</literal> or array for this provider to operate. It will remove any
  289. element if the <literal>AclManager</literal> indicates the
  290. <interfacename>Authentication</interfacename> does not hold one of the listed
  291. <literal>requirePermission</literal>s.</para>
  292. <para>The Contacts sample application demonstrates these two
  293. <literal>AfterInvocationProvider</literal>s.</para>
  294. </section> -->
  295. </section>
  296. <section xml:id="authz-hierarchical-roles">
  297. <title>Hierarchical Roles</title>
  298. <para>
  299. It is a common requirement that a particular role in an application should automatically
  300. <quote>include</quote> other roles. For example, in an application which has the concept of
  301. an <quote>admin</quote> and a <quote>user</quote> role, you may want an admin to be able to
  302. do everything a normal user can. To achieve this, you can either make sure that all admin users
  303. are also assigned the <quote>user</quote> role. Alternatively, you can modify every access constraint
  304. which requires the <quote>user</quote> role to also include the <quote>admin</quote> role.
  305. This can get quite complicated if you have a lot of different roles in your application.
  306. </para>
  307. <para>
  308. The use of a role-hierarchy allows you to configure which roles (or authorities) should include others.
  309. An extended version of Spring Security's <link linkend="authz-role-voter"><classname>RoleVoter</classname></link>,
  310. <classname>RoleHierarchyVoter</classname>, is configured with a <interfacename>RoleHierarchy</interfacename>,
  311. from which it obtains all the <quote>reachable authorities</quote> which the user is assigned.
  312. A typical configuration might look like this:
  313. <programlisting language="xml"><![CDATA[
  314. <bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
  315. <constructor-arg ref="roleHierarchy" />
  316. </bean>
  317. <bean id="roleHierarchy"
  318. class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
  319. <property name="hierarchy">
  320. <value>
  321. ROLE_ADMIN > ROLE_STAFF
  322. ROLE_STAFF > ROLE_USER
  323. ROLE_USER > ROLE_GUEST
  324. </value>
  325. </property>
  326. </bean>]]>
  327. </programlisting>
  328. Here we have four roles in a hierarchy <literal>ROLE_ADMIN => ROLE_STAFF => ROLE_USER => ROLE_GUEST</literal>.
  329. A user who is authenticated with <literal>ROLE_ADMIN</literal>, will behave as if they have all four roles when
  330. security contraints are evaluated against an <interfacename>AccessDecisionManager</interfacename> cconfigured
  331. with the above <classname>RoleHierarchyVoter</classname>. The <literal>&gt;</literal> symbol can be thought of
  332. as meaning <quote>includes</quote>.
  333. </para>
  334. <para>
  335. Role hierarchies offer a convenient means of simplifying the access-control configuration data for your
  336. application and/or reducing the number of authorities which you need to assign to a user. For more
  337. complex requirements you may wish to define a logical mapping between the specific access-rights your
  338. application requires and the roles that are assigned to users, translating between the two when loading
  339. the user information.
  340. <!-- TODO: Extend when authority-mapping layer is added -->
  341. </para>
  342. </section>
  343. </chapter>