core-services.xml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="core-services"
  2. xmlns:xlink="http://www.w3.org/1999/xlink">
  3. <title>Core Services</title>
  4. <para> Now that we have a high-level overview of the Spring Security architecture and its core
  5. classes, let's take a closer look at one or two of the core interfaces and their
  6. implementations, in particular the <interfacename>AuthenticationManager</interfacename>,
  7. <interfacename>UserDetailsService</interfacename> and the
  8. <interfacename>AccessDecisionManager</interfacename>. These crop up regularly throughout the
  9. remainder of this document so it's important you know how they are configured and how they
  10. operate. </para>
  11. <section xml:id="core-services-authentication-manager">
  12. <title>The <interfacename>AuthenticationManager</interfacename>,
  13. <classname>ProviderManager</classname> and
  14. <classname>AuthenticationProvider</classname>s</title>
  15. <para>The <interfacename>AuthenticationManager</interfacename> is just an interface, so the
  16. implementation can be anything we choose, but how does it work in practice? What if we
  17. need to check multiple authentication databases or a combination of different
  18. authentication services such as a database and an LDAP server?</para>
  19. <para>The default implementation in Spring Security is called
  20. <classname>ProviderManager</classname> and rather than handling the authentication
  21. request itself, it delegates to a list of configured
  22. <classname>AuthenticationProvider</classname>s, each of which is queried in turn to see
  23. if it can perform the authentication. Each provider will either throw an exception or
  24. return a fully populated <interfacename>Authentication</interfacename> object. Remember
  25. our good friends, <interfacename>UserDetails</interfacename> and
  26. <interfacename>UserDetailsService</interfacename>? If not, head back to the previous
  27. chapter and refresh your memory. The most common approach to verifying an authentication
  28. request is to load the corresponding <interfacename>UserDetails</interfacename> and
  29. check the loaded password against the one that has been entered by the user. This is the
  30. approach used by the <classname>DaoAuthenticationProvider</classname> (see below). The
  31. loaded <interfacename>UserDetails</interfacename> object - and particularly the
  32. <literal>GrantedAuthority</literal>s it contains - will be used when building the fully
  33. populated <interfacename>Authentication</interfacename> object which is returned from a
  34. successful authentication and stored in the <classname>SecurityContext</classname>. </para>
  35. <para> If you are using the namespace, an instance of <classname>ProviderManager</classname>
  36. is created and maintained internally, and you add providers to it by using the namespace
  37. authentication provider elements (see <link xlink:href="#ns-auth-manager">the namespace
  38. chapter</link>). In this case, you should not declare a
  39. <classname>ProviderManager</classname> bean in your application context. However, if you
  40. are not using the namespace then you would declare it like so: <programlisting language="xml"><![CDATA[
  41. <bean id="authenticationManager"
  42. class="org.springframework.security.authentication.ProviderManager">
  43. <property name="providers">
  44. <list>
  45. <ref local="daoAuthenticationProvider"/>
  46. <ref local="anonymousAuthenticationProvider"/>
  47. <ref local="ldapAuthenticationProvider"/>
  48. </list>
  49. </property>
  50. </bean>]]></programlisting></para>
  51. <para>In the above example we have three providers. They are tried in the order shown (which
  52. is implied by the use of a <literal>List</literal>), with each provider able to attempt
  53. authentication, or skip authentication by simply returning <literal>null</literal>. If
  54. all implementations return null, the <literal>ProviderManager</literal> will throw a
  55. <exceptionname>ProviderNotFoundException</exceptionname>. If you're interested in
  56. learning more about chaining providers, please refer to the
  57. <literal>ProviderManager</literal> JavaDocs.</para>
  58. <para> Authentication mechanisms such as a web form-login processing filter are injected
  59. with a reference to the <interfacename>ProviderManager</interfacename> and will call it
  60. to handle their authentication requests. The providers you require will sometimes be
  61. interchangeable with the authentication mechanisms, while at other times they will
  62. depend on a specific authentication mechanism. For example,
  63. <classname>DaoAuthenticationProvider</classname> and
  64. <classname>LdapAuthenticationProvider</classname> are compatible with any mechanism
  65. which submits a simple username/password authentication request and so will work with
  66. form-based logins or HTTP Basic authentication. On the other hand, some authentication
  67. mechanisms create an authentication request object which can only be interpreted by a
  68. single type of <classname>AuthenticationProvider</classname>. An example of this would
  69. be JA-SIG CAS, which uses the notion of a service ticket and so can therefore only be
  70. authenticated by a <classname>CasAuthenticationProvider</classname>. You needn't be too
  71. concerned about this, because if you forget to register a suitable provider, you'll
  72. simply receive a <literal>ProviderNotFoundException</literal> when an attempt to
  73. authenticate is made.</para>
  74. <section xml:id="core-services-dao-provider">
  75. <title><literal>DaoAuthenticationProvider</literal></title>
  76. <para>The simplest <interfacename>AuthenticationProvider</interfacename> implemented by
  77. Spring Security is <literal>DaoAuthenticationProvider</literal>, which is also one
  78. of the earliest supported by the framework. It leverages a
  79. <interfacename>UserDetailsService</interfacename> (as a DAO) in order to lookup the
  80. username, password and <interfacename>GrantedAuthority</interfacename>s. It
  81. authenticates the user simply by comparing the password submitted in a
  82. <classname>UsernamePasswordAuthenticationToken</classname> against the one loaded by
  83. the <interfacename>UserDetailsService</interfacename>. Configuring the provider is
  84. quite simple: <programlisting language="xml"><![CDATA[
  85. <bean id="daoAuthenticationProvider"
  86. class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
  87. <property name="userDetailsService" ref="inMemoryDaoImpl"/>
  88. <property name="saltSource" ref bean="saltSource"/>
  89. <property name="passwordEncoder" ref="passwordEncoder"/>
  90. </bean>]]></programlisting> The <interfacename>PasswordEncoder</interfacename> and
  91. <interfacename>SaltSource</interfacename> are optional. A
  92. <interfacename>PasswordEncoder</interfacename> provides encoding and decoding of
  93. passwords presented in the <interfacename>UserDetails</interfacename> object that is
  94. returned from the configured <interfacename>UserDetailsService</interfacename>. A
  95. <interfacename>SaltSource</interfacename> enables the passwords to be populated with
  96. a "salt", which enhances the security of the passwords in the authentication
  97. repository. These will be discussed in more detail <link
  98. xlink:href="core-services-password-encodin">below</link>. </para>
  99. </section>
  100. </section>
  101. <section>
  102. <title><interfacename>UserDetailsService</interfacename> Implementations</title>
  103. <para>As mentioned in the earlier in this reference guide, most authentication providers
  104. take advantage of the <interfacename>UserDetails</interfacename> and
  105. <interfacename>UserDetailsService</interfacename> interfaces. Recall that the contract
  106. for <interfacename>UserDetailsService</interfacename> is a single method:</para>
  107. <para>
  108. <programlisting>
  109. UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
  110. </programlisting> </para>
  111. <para>The returned <interfacename>UserDetails</interfacename> is an interface that provides
  112. getters that guarantee non-null provision of authentication information such as the
  113. username, password, granted authorities and whether the user account is enabled or
  114. disabled. Most authentication providers will use a
  115. <interfacename>UserDetailsService</interfacename>, even if the username and password are
  116. not actually used as part of the authentication decision. They may use the returned
  117. <interfacename>UserDetails</interfacename> object just for its
  118. <literal>GrantedAuthority</literal> information, because some other system (like LDAP or
  119. X.509 or CAS etc) has undertaken the responsibility of actually validating the
  120. credentials.</para>
  121. <para>Given <interfacename>UserDetailsService</interfacename> is so simple to implement, it
  122. should be easy for users to retrieve authentication information using a persistence
  123. strategy of their choice. Having said that, Spring Security does include a couple of
  124. useful base implementations, which we'll look at below.</para>
  125. <section xml:id="core-services-in-memory-service">
  126. <title>In-Memory Authentication</title>
  127. <para>Is easy to use create a custom <interfacename>UserDetailsService</interfacename>
  128. implementation that extracts information from a persistence engine of choice, but
  129. many applications do not require such complexity. This is particularly true if
  130. you're building a prototype application or just starting integrating Spring
  131. Security, when you don't really want to spend time configuring databases or writing
  132. <interfacename>UserDetailsService</interfacename> implementations. For this sort of
  133. situation, a simple option is to use the <literal>user-service</literal> element
  134. from the security <link xlink:href="#ns-minimal">namespace</link>: <programlisting><![CDATA[
  135. <user-service id="userDetailsService">
  136. <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
  137. <user name="bob" password="bobspassword" authorities="ROLE_USER" />
  138. </user-service>
  139. ]]>
  140. </programlisting> This also supports the use of an external properties
  141. file: <programlisting><![CDATA[
  142. <user-service id="userDetailsService" properties="users.properties"/>
  143. ]]></programlisting> The properties file should contain entries in the form
  144. <programlisting>username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]</programlisting>
  145. For example
  146. <programlisting>
  147. jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled
  148. bob=bobspassword,ROLE_USER,enabled</programlisting></para>
  149. </section>
  150. <section xml:id="core-services-jdbc-user-service">
  151. <title><literal>JdbcDaoImpl</literal></title>
  152. <para>Spring Security also includes a <interfacename>UserDetailsService</interfacename>
  153. that can obtain authentication information from a JDBC data source. Internally
  154. Spring JDBC is used, so it avoids the complexity of a fully-featured object
  155. relational mapper (ORM) just to store user details. If your application does use an
  156. ORM tool, you might prefer to write a custom
  157. <interfacename>UserDetailsService</interfacename> to reuse the mapping files you've
  158. probably already created. Returning to <literal>JdbcDaoImpl</literal>, an example
  159. configuration is shown below:</para>
  160. <para> <programlisting language="xml"><![CDATA[
  161. <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  162. <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
  163. <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
  164. <property name="username" value="sa"/>
  165. <property name="password" value=""/>
  166. </bean>
  167. <bean id="userDetailsService"
  168. class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
  169. <property name="dataSource" ref="dataSource"/>
  170. </bean> ]]> </programlisting> </para>
  171. <para>You can use different relational database management systems by modifying the
  172. <literal>DriverManagerDataSource</literal> shown above. You can also use a global
  173. data source obtained from JNDI, as with any other Spring configuration.</para>
  174. <section>
  175. <title>Authority Groups</title>
  176. <para>By default, <classname>JdbcDaoImpl</classname> loads the authorities for a
  177. single user with the assumption that the authorities are mapped directly to
  178. users (see the <link xlink:href="#appendix-schema">database schema
  179. appendix</link>). An alternative approach is to partition the authorities into
  180. groups and assign groups to the user. Some people prefer this approach as a
  181. means of administering user rights. See the <classname>JdbcDaoImpl</classname>
  182. Javadoc for more information on how to enable the use of group authorities. The
  183. group schema is also included in the appendix.</para>
  184. </section>
  185. <!--
  186. <para>If the default schema is unsuitable for your needs, <literal>JdbcDaoImpl</literal>
  187. provides properties that allow customisation of the SQL statements. Please refer to the
  188. JavaDocs for details, but note that the class is not intended for complex custom
  189. subclasses. If you have a complex schema or would like a custom
  190. <interfacename>UserDetails</interfacename> implementation returned, you'd be better off
  191. writing your own <interfacename>UserDetailsService</interfacename>. The base
  192. implementation provided with Spring Security is intended for typical situations, rather
  193. than catering for all possible requirements.</para>
  194. -->
  195. </section>
  196. </section>
  197. <section xml:id="core-services-password-encoding">
  198. <title>Password Encoding</title>
  199. <para>Spring Security's <interfacename>PasswordEncoder</interfacename> interface is used to
  200. support the use of passwords which are encoded in some way in persistent storage. This
  201. will normally mean that the passwords are <quote>hashed</quote> using a digest algorithm
  202. such as MD5 or SHA.</para>
  203. <section>
  204. <title>What is a hash?</title>
  205. <para>Password hashing is not unique to Spring Security but is a common source of
  206. confusion for users who are not familiar with the concept. A hash (or digest)
  207. algorithm is a one-way function which produces a piece of fixed-length output data
  208. (the hash) from some input data, such as a password. As an example, the MD5 hash of
  209. the string <quote>password</quote> (in hexadecimal) is
  210. <programlisting>
  211. 5f4dcc3b5aa765d61d8327deb882cf99
  212. </programlisting> A hash is
  213. <quote>one-way</quote> in the sense that it is very difficult (effectively
  214. impossible) to obtain the original input given the hash value, or indeed any
  215. possible input which would produce that hash value. This property makes hash values
  216. very useful for authentication purposes. They can be stored in your user database as
  217. an alternative to plaintext passwords and even if the values are compromised they do
  218. not immediately reveal a password which can be used to login. Note that this also
  219. means you have no way of recovering the password once it is encoded.</para>
  220. </section>
  221. <section>
  222. <title>Adding Salt to a Hash</title>
  223. <para> One potential problem with the use of password hashes that it is relatively easy
  224. to get round the one-way property of the hash if a common word is used for the
  225. input. For example, if you search for the hash value
  226. <literal>5f4dcc3b5aa765d61d8327deb882cf99</literal> using google, you will quickly
  227. find the original word <quote>password</quote>. In a similar way, an attacker can
  228. build a dictionary of hashes from a standard word list and use this to lookup the
  229. original password. One way to help prevent this is to have a suitably strong
  230. password policy to try to prevent common words from being used. Another is to use a
  231. <quote>salt</quote> when calculating the hashes. This is an additional string of
  232. known data for each user which is combined with the password before calculating the
  233. hash. Ideally the data should be as random as possible, but in practice any salt
  234. value is usually preferable to none. Spring Security has a
  235. <interfacename>SaltSource</interfacename> interface which can be used by an
  236. authentication provider to generate a salt value for a particular user. Using a salt
  237. means that an attacker has to build a separate dictionary of hashes for each salt
  238. value, making the attack more complicated (but not impossible).</para>
  239. </section>
  240. <section>
  241. <title> Hashing and Authentication</title>
  242. <para>When an authentication provider (such as Spring Security's
  243. <classname>DaoAuthenticationProvider</classname> needs to check the password in a
  244. submitted authentication request against the known value for a user, and the stored
  245. password is encoded in some way, then the submitted value must be encoded using
  246. exactly the same algorithm. It's up to you to check that these are compatible as
  247. Spring Security has no control over the persistent values. If you add password
  248. hashing to your authentication configuration in Spring Security, and your database
  249. contains plaintext passwords, then there is no way authentication can succeed. Even
  250. if you are aware that your database is using MD5 to encode the passwords, for
  251. example, and your application is configured to use Spring Security's
  252. <classname>Md5PasswordEncoder</classname>, there are still things that can go wrong.
  253. The database may have the passwords encoded in Base 64, for example while the
  254. enocoder is using hexadecimal strings (the default)<footnote>
  255. <para>You can configure the encoder to use Base 64 instead of hex by setting the
  256. <literal>encodeHashAsBase64</literal> property to <literal>true</literal>. Check
  257. the Javadoc for <classname>MessageDigestPasswordEncoder</classname> and its
  258. parent classes for more information.</para>
  259. </footnote>. Alternatively your database may be using upper-case while the output
  260. from the encoder is lower-case. Make sure you write a test to check the output from
  261. your configured password encoder with a known password and salt combination and
  262. check that it matches the database value before going further and attempting to
  263. authenticate through your application. For more information on the default method
  264. for merging salt and password, see the Javadoc for
  265. <classname>BasePasswordEncoder</classname>. If you want to generate encoded
  266. passwords directly in Java for storage in your user database, then you can use the
  267. <methodname>encodePassword</methodname> method on the
  268. <interfacename>PasswordEncoder</interfacename>.</para>
  269. </section>
  270. </section>
  271. </chapter>