2
0

servlet-api.adoc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. [[servletapi]]
  2. = Servlet API integration
  3. This section describes how Spring Security is integrated with the Servlet API.
  4. [[servletapi-25]]
  5. == Servlet 2.5+ Integration
  6. This section describes how Spring Security integrates with the Servlet 2.5 specification.
  7. [[servletapi-remote-user]]
  8. === HttpServletRequest.getRemoteUser()
  9. https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[`HttpServletRequest.getRemoteUser()`] returns the result of `SecurityContextHolder.getContext().getAuthentication().getName()`, which is typically the current username.This can be useful if you want to display the current username in your application.
  10. Additionally, you can check this for null to determine whether a user has authenticated or is anonymous.
  11. Knowing whether the user is authenticated or not can be useful for determining if certain UI elements should be shown or not (for example, a logout link that should be displayed only if the user is authenticated).
  12. [[servletapi-user-principal]]
  13. === HttpServletRequest.getUserPrincipal()
  14. https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[`HttpServletRequest.getUserPrincipal()`] returns the result of `SecurityContextHolder.getContext().getAuthentication()`.
  15. This means that it is an `Authentication`, which is typically an instance of `UsernamePasswordAuthenticationToken` when using username- and password-based authentication.
  16. This can be useful if you need additional information about your user.
  17. For example, you might have created a custom `UserDetailsService` that returns a custom `UserDetails` containing a first and last name for your user.
  18. You could obtain this information with the following:
  19. [tabs]
  20. ======
  21. Java::
  22. +
  23. [source,java,role="primary"]
  24. ----
  25. Authentication auth = httpServletRequest.getUserPrincipal();
  26. // assume integrated custom UserDetails called MyCustomUserDetails
  27. // by default, typically instance of UserDetails
  28. MyCustomUserDetails userDetails = (MyCustomUserDetails) auth.getPrincipal();
  29. String firstName = userDetails.getFirstName();
  30. String lastName = userDetails.getLastName();
  31. ----
  32. Kotlin::
  33. +
  34. [source,kotlin,role="secondary"]
  35. ----
  36. val auth: Authentication = httpServletRequest.getUserPrincipal()
  37. // assume integrated custom UserDetails called MyCustomUserDetails
  38. // by default, typically instance of UserDetails
  39. val userDetails: MyCustomUserDetails = auth.principal as MyCustomUserDetails
  40. val firstName: String = userDetails.firstName
  41. val lastName: String = userDetails.lastName
  42. ----
  43. ======
  44. [NOTE]
  45. ====
  46. It should be noted that it is typically bad practice to perform so much logic throughout your application.
  47. Instead, one should centralize it to reduce any coupling of Spring Security and the Servlet API's.
  48. ====
  49. [[servletapi-user-in-role]]
  50. === HttpServletRequest.isUserInRole(String)
  51. https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[`HttpServletRequest.isUserInRole(String)`] determines if `SecurityContextHolder.getContext().getAuthentication().getAuthorities()` contains a `GrantedAuthority` with the role passed into `isUserInRole(String)`.
  52. Typically, users should not pass the `ROLE_` prefix to this method, since it is added automatically.
  53. For example, if you want to determine if the current user has the authority "ROLE_ADMIN", you could use the following:
  54. [tabs]
  55. ======
  56. Java::
  57. +
  58. [source,java,role="primary"]
  59. ----
  60. boolean isAdmin = httpServletRequest.isUserInRole("ADMIN");
  61. ----
  62. Kotlin::
  63. +
  64. [source,kotlin,role="secondary"]
  65. ----
  66. val isAdmin: Boolean = httpServletRequest.isUserInRole("ADMIN")
  67. ----
  68. ======
  69. This might be useful to determine if certain UI components should be displayed.
  70. For example, you might display admin links only if the current user is an admin.
  71. [[servletapi-3]]
  72. == Servlet 3+ Integration
  73. The following section describes the Servlet 3 methods with which Spring Security integrates.
  74. [[servletapi-authenticate]]
  75. === HttpServletRequest.authenticate(HttpServletResponse)
  76. You can use the https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#authenticate%28javax.servlet.http.HttpServletResponse%29[`HttpServletRequest.authenticate(HttpServletResponse)`] method to ensure that a user is authenticated.
  77. If they are not authenticated, the configured `AuthenticationEntryPoint` is used to request the user to authenticate (redirect to the login page).
  78. [[servletapi-login]]
  79. === HttpServletRequest.login(String,String)
  80. You can use the https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login%28java.lang.String,%20java.lang.String%29[`HttpServletRequest.login(String,String)`] method to authenticate the user with the current `AuthenticationManager`.
  81. For example, the following would attempt to authenticate with a username of `user` and a password of `password`:
  82. [tabs]
  83. ======
  84. Java::
  85. +
  86. [source,java,role="primary"]
  87. ----
  88. try {
  89. httpServletRequest.login("user","password");
  90. } catch(ServletException ex) {
  91. // fail to authenticate
  92. }
  93. ----
  94. Kotlin::
  95. +
  96. [source,kotlin,role="secondary"]
  97. ----
  98. try {
  99. httpServletRequest.login("user", "password")
  100. } catch (ex: ServletException) {
  101. // fail to authenticate
  102. }
  103. ----
  104. ======
  105. [NOTE]
  106. ====
  107. You need not catch the `ServletException` if you want Spring Security to process the failed authentication attempt.
  108. ====
  109. [[servletapi-logout]]
  110. === HttpServletRequest.logout()
  111. You can use the https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout%28%29[`HttpServletRequest.logout()`] method to log out the current user.
  112. Typically, this means that the `SecurityContextHolder` is cleared out, the `HttpSession` is invalidated, any "`Remember Me`" authentication is cleaned up, and so on.
  113. However, the configured `LogoutHandler` implementations vary, depending on your Spring Security configuration.
  114. Note that, after `HttpServletRequest.logout()` has been invoked, you are still in charge of writing out a response.
  115. Typically, this would involve a redirect to the welcome page.
  116. [[servletapi-start-runnable]]
  117. === AsyncContext.start(Runnable)
  118. The https://docs.oracle.com/javaee/6/api/javax/servlet/AsyncContext.html#start%28java.lang.Runnable%29[`AsyncContext.start(Runnable)`] method ensures your credentials are propagated to the new `Thread`.
  119. By using Spring Security's concurrency support, Spring Security overrides `AsyncContext.start(Runnable)` to ensure that the current `SecurityContext` is used when processing the Runnable.
  120. The following example outputs the current user's Authentication:
  121. [tabs]
  122. ======
  123. Java::
  124. +
  125. [source,java,role="primary"]
  126. ----
  127. final AsyncContext async = httpServletRequest.startAsync();
  128. async.start(new Runnable() {
  129. public void run() {
  130. Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  131. try {
  132. final HttpServletResponse asyncResponse = (HttpServletResponse) async.getResponse();
  133. asyncResponse.setStatus(HttpServletResponse.SC_OK);
  134. asyncResponse.getWriter().write(String.valueOf(authentication));
  135. async.complete();
  136. } catch(Exception ex) {
  137. throw new RuntimeException(ex);
  138. }
  139. }
  140. });
  141. ----
  142. Kotlin::
  143. +
  144. [source,kotlin,role="secondary"]
  145. ----
  146. val async: AsyncContext = httpServletRequest.startAsync()
  147. async.start {
  148. val authentication: Authentication = SecurityContextHolder.getContext().authentication
  149. try {
  150. val asyncResponse = async.response as HttpServletResponse
  151. asyncResponse.status = HttpServletResponse.SC_OK
  152. asyncResponse.writer.write(String.valueOf(authentication))
  153. async.complete()
  154. } catch (ex: Exception) {
  155. throw RuntimeException(ex)
  156. }
  157. }
  158. ----
  159. ======
  160. [[servletapi-async]]
  161. === Async Servlet Support
  162. If you use Java-based configuration, you are ready to go.
  163. If you use XML configuration, a few updates are necessary.
  164. The first step is to ensure that you have updated your `web.xml` file to use at least the 3.0 schema:
  165. [source,xml]
  166. ----
  167. <web-app xmlns="http://java.sun.com/xml/ns/javaee"
  168. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  169. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  170. version="3.0">
  171. </web-app>
  172. ----
  173. Next, you need to ensure that your `springSecurityFilterChain` is set up for processing asynchronous requests:
  174. [source,xml]
  175. ----
  176. <filter>
  177. <filter-name>springSecurityFilterChain</filter-name>
  178. <filter-class>
  179. org.springframework.web.filter.DelegatingFilterProxy
  180. </filter-class>
  181. <async-supported>true</async-supported>
  182. </filter>
  183. <filter-mapping>
  184. <filter-name>springSecurityFilterChain</filter-name>
  185. <url-pattern>/*</url-pattern>
  186. <dispatcher>REQUEST</dispatcher>
  187. <dispatcher>ASYNC</dispatcher>
  188. </filter-mapping>
  189. ----
  190. Now Spring Security ensures that your `SecurityContext` is propagated on asynchronous requests, too.
  191. So how does it work? If you are not really interested, feel free to skip the remainder of this section
  192. Most of this is built into the Servlet specification, but there is a little bit of tweaking that Spring Security does to ensure things work properly with asynchronous requests.
  193. Prior to Spring Security 3.2, the `SecurityContext` from the `SecurityContextHolder` was automatically saved as soon as the `HttpServletResponse` was committed.
  194. This can cause issues in an asynchronous environment.
  195. Consider the following example:
  196. [tabs]
  197. ======
  198. Java::
  199. +
  200. [source,java,role="primary"]
  201. ----
  202. httpServletRequest.startAsync();
  203. new Thread("AsyncThread") {
  204. @Override
  205. public void run() {
  206. try {
  207. // Do work
  208. TimeUnit.SECONDS.sleep(1);
  209. // Write to and commit the httpServletResponse
  210. httpServletResponse.getOutputStream().flush();
  211. } catch (Exception ex) {
  212. ex.printStackTrace();
  213. }
  214. }
  215. }.start();
  216. ----
  217. Kotlin::
  218. +
  219. [source,kotlin,role="secondary"]
  220. ----
  221. httpServletRequest.startAsync()
  222. object : Thread("AsyncThread") {
  223. override fun run() {
  224. try {
  225. // Do work
  226. TimeUnit.SECONDS.sleep(1)
  227. // Write to and commit the httpServletResponse
  228. httpServletResponse.outputStream.flush()
  229. } catch (ex: java.lang.Exception) {
  230. ex.printStackTrace()
  231. }
  232. }
  233. }.start()
  234. ----
  235. ======
  236. The issue is that this `Thread` is not known to Spring Security, so the `SecurityContext` is not propagated to it.
  237. This means that, when we commit the `HttpServletResponse`, there is no `SecurityContext`.
  238. When Spring Security automatically saved the `SecurityContext` on committing the `HttpServletResponse`, it would lose a logged in user.
  239. Since version 3.2, Spring Security is smart enough to no longer automatically save the `SecurityContext` on committing the `HttpServletResponse` as soon as `HttpServletRequest.startAsync()` is invoked.
  240. [[servletapi-31]]
  241. == Servlet 3.1+ Integration
  242. The following section describes the Servlet 3.1 methods that Spring Security integrates with.
  243. [[servletapi-change-session-id]]
  244. === HttpServletRequest#changeSessionId()
  245. https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#changeSessionId()[HttpServletRequest.changeSessionId()] is the default method for protecting against xref:servlet/authentication/session-management.adoc#ns-session-fixation[Session Fixation] attacks in Servlet 3.1 and higher.