form.adoc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. [[servlet-authentication-form]]
  2. = Form Login
  3. Spring Security provides support for username and password being provided through an html form.
  4. This section provides details on how form based authentication works within Spring Security.
  5. // FIXME: describe authenticationentrypoint, authenticationfailurehandler, authenticationsuccesshandler
  6. Let's take a look at how form based log in works within Spring Security.
  7. First, we see how the user is redirected to the log in form.
  8. .Redirecting to the Log In Page
  9. image::{figures}/loginurlauthenticationentrypoint.png[]
  10. The figure builds off our <<servlet-securityfilterchain,`SecurityFilterChain`>> diagram.
  11. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized.
  12. image:{icondir}/number_2.png[] Spring Security's <<servlet-authorization-filtersecurityinterceptor,`FilterSecurityInterceptor`>> indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`.
  13. image:{icondir}/number_3.png[] Since the user is not authenticated, <<servlet-exceptiontranslationfilter,`ExceptionTranslationFilter`>> initiates __Start Authentication__ and sends a redirect to the log in page with the configured <<servlet-authentication-authenticationentrypoint,`AuthenticationEntryPoint`>>.
  14. In most cases the `AuthenticationEntryPoint` is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`].
  15. image:{icondir}/number_4.png[] The browser will then request the log in page that it was redirected to.
  16. image:{icondir}/number_5.png[] Something within the application, must <<servlet-authentication-form-custom,render the log in page>>.
  17. [[servlet-authentication-usernamepasswordauthenticationfilter]]
  18. When the username and password are submitted, the `UsernamePasswordAuthenticationFilter` authenticates the username and password.
  19. The `UsernamePasswordAuthenticationFilter` extends <<servlet-authentication-abstractprocessingfilter>>, so this diagram should look pretty similar.
  20. .Authenticating Username and Password
  21. image::{figures}/usernamepasswordauthenticationfilter.png[]
  22. The figure builds off our <<servlet-securityfilterchain,`SecurityFilterChain`>> diagram.
  23. image:{icondir}/number_1.png[] When the user submits their username and password, the `UsernamePasswordAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of <<servlet-authentication-authentication,`Authentication`>> by extracting the username and password from the `HttpServletRequest`.
  24. image:{icondir}/number_2.png[] Next, the `UsernamePasswordAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated.
  25. The details of what `AuthenticationManager` looks like depend on how the <<servlet-authentication-unpwd-storage,user information is stored>>.
  26. image:{icondir}/number_3.png[] If authentication fails, then __Failure__
  27. * The <<servlet-authentication-securitycontextholder>> is cleared out.
  28. * `RememberMeServices.loginFail` is invoked.
  29. If remember me is not configured, this is a no-op.
  30. // FIXME: link to rememberme
  31. * `AuthenticationFailureHandler` is invoked.
  32. // FIXME: link to AuthenticationFailureHandler
  33. image:{icondir}/number_4.png[] If authentication is successful, then __Success__.
  34. * `SessionAuthenticationStrategy` is notified of a new log in.
  35. // FIXME: Add link to SessionAuthenticationStrategy
  36. * The <<servlet-authentication-authentication>> is set on the <<servlet-authentication-securitycontextholder>>.
  37. // FIXME: link securitycontextpersistencefilter
  38. * `RememberMeServices.loginSuccess` is invoked.
  39. If remember me is not configured, this is a no-op.
  40. // FIXME: link to rememberme
  41. * `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`.
  42. * The `AuthenticationSuccessHandler` is invoked. Typically this is a `SimpleUrlAuthenticationSuccessHandler` which will redirect to a request saved by <<servlet-exceptiontranslationfilter,`ExceptionTranslationFilter`>> when we redirect to the log in page.
  43. [[servlet-authentication-form-min]]
  44. Spring Security form log in is enabled by default.
  45. However, as soon as any servlet based configuration is provided, form based log in must be explicitly provided.
  46. A minimal, explicit Java configuration can be found below:
  47. .Form Log In
  48. ====
  49. .Java
  50. [source,java,role="primary"]
  51. ----
  52. protected void configure(HttpSecurity http) {
  53. http
  54. // ...
  55. .formLogin(withDefaults());
  56. }
  57. ----
  58. .XML
  59. [source,xml,role="secondary"]
  60. ----
  61. <http>
  62. <!-- ... -->
  63. <form-login />
  64. </http>
  65. ----
  66. .Kotlin
  67. [source,kotlin,role="secondary"]
  68. ----
  69. fun configure(http: HttpSecurity) {
  70. http {
  71. // ...
  72. formLogin { }
  73. }
  74. }
  75. ----
  76. ====
  77. In this configuration Spring Security will render a default log in page.
  78. Most production applications will require a custom log in form.
  79. [[servlet-authentication-form-custom]]
  80. The configuration below demonstrates how to provide a custom log in form.
  81. .Custom Log In Form Configuration
  82. ====
  83. .Java
  84. [source,java,role="primary"]
  85. ----
  86. protected void configure(HttpSecurity http) throws Exception {
  87. http
  88. // ...
  89. .formLogin(form -> form
  90. .loginPage("/login")
  91. .permitAll()
  92. );
  93. }
  94. ----
  95. .XML
  96. [source,xml,role="secondary"]
  97. ----
  98. <http>
  99. <!-- ... -->
  100. <intercept-url pattern="/login" access="permitAll" />
  101. <form-login login-page="/login" />
  102. </http>
  103. ----
  104. .Kotlin
  105. [source,kotlin,role="secondary"]
  106. ----
  107. fun configure(http: HttpSecurity) {
  108. http {
  109. // ...
  110. formLogin {
  111. loginPage = "/login"
  112. permitAll()
  113. }
  114. }
  115. }
  116. ----
  117. ====
  118. [[servlet-authentication-form-custom-html]]
  119. When the login page is specified in the Spring Security configuration, you are responsible for rendering the page.
  120. // FIXME: default login page rendered by Spring Security
  121. Below is a https://www.thymeleaf.org/[Thymeleaf] template that produces an HTML login form that complies with a login page of `/login`:
  122. .Log In Form
  123. ====
  124. .src/main/resources/templates/login.html
  125. [source,xml]
  126. ----
  127. <!DOCTYPE html>
  128. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
  129. <head>
  130. <title>Please Log In</title>
  131. </head>
  132. <body>
  133. <h1>Please Log In</h1>
  134. <div th:if="${param.error}">
  135. Invalid username and password.</div>
  136. <div th:if="${param.logout}">
  137. You have been logged out.</div>
  138. <form th:action="@{/login}" method="post">
  139. <div>
  140. <input type="text" name="username" placeholder="Username"/>
  141. </div>
  142. <div>
  143. <input type="password" name="password" placeholder="Password"/>
  144. </div>
  145. <input type="submit" value="Log in" />
  146. </form>
  147. </body>
  148. </html>
  149. ----
  150. ====
  151. There are a few key points about the default HTML form:
  152. * The form should perform a `post` to `/login`
  153. * The form will need to include a <<servlet-csrf,CSRF Token>> which is <<servlet-csrf-include-form-auto,automatically included>> by Thymeleaf.
  154. * The form should specify the username in a parameter named `username`
  155. * The form should specify the password in a parameter named `password`
  156. * If the HTTP parameter error is found, it indicates the user failed to provide a valid username / password
  157. * If the HTTP parameter logout is found, it indicates the user has logged out successfully
  158. Many users will not need much more than to customize the log in page.
  159. However, if needed, everything above can be customized with additional configuration.
  160. [[servlet-authentication-form-custom-controller]]
  161. If you are using Spring MVC, you will need a controller that maps `GET /login` to the login template we created.
  162. A minimal sample `LoginController` can be seen below:
  163. .LoginController
  164. ====
  165. .Java
  166. [source,java,role="primary"]
  167. ----
  168. @Controller
  169. class LoginController {
  170. @GetMapping("/login")
  171. String login() {
  172. return "login";
  173. }
  174. }
  175. ----
  176. .Kotlin
  177. [source,kotlin,role="secondary"]
  178. ----
  179. @Controller
  180. class LoginController {
  181. @GetMapping("/login")
  182. fun login(): String {
  183. return "login"
  184. }
  185. }
  186. ----
  187. ====