|
@@ -0,0 +1,167 @@
|
|
|
+<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="servletapi"
|
|
|
+ xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
|
+ <info>
|
|
|
+ <title>Servlet API integration</title>
|
|
|
+ </info>
|
|
|
+ <para>This section describes how Spring Security is integrated with the Servlet API. The
|
|
|
+ <link xlink:href="https://github.com/SpringSource/spring-security/blob/master/samples/servletapi-xml">servletapi-xml</link> sample application demonstrates
|
|
|
+ the usage of each of these methods.</para>
|
|
|
+ <section xml:id="servletapi-25">
|
|
|
+ <title>Servlet 2.5+ Integration</title>
|
|
|
+ <section xml:id="servletapi-remote-user">
|
|
|
+ <title>HttpServletRequest.getRemoteUser()</title>
|
|
|
+ <para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()">HttpServletRequest.getRemoteUser()</link>
|
|
|
+ will return the result of <literal>SecurityContextHolder.getContext().getAuthentication().getName()</literal> which is typically the current
|
|
|
+ username. This can be useful if you want to display the current username in your application. Additionally, checking if this
|
|
|
+ is null can be used to indicate if a user has authenticated or is anonymous. Knowing if the user is authenticated or not can
|
|
|
+ be useful for determining if certain UI elements should be shown or not (i.e. a log out link should only be displayed if the
|
|
|
+ user is authenticated).</para>
|
|
|
+ </section>
|
|
|
+ <section xml:id="servletapi-user-principal">
|
|
|
+ <title>HttpServletRequest.getUserPrincipal()</title>
|
|
|
+ <para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()">HttpServletRequest.getUserPrincipal()</link>
|
|
|
+ will return the result of <literal>SecurityContextHolder.getContext().getAuthentication()</literal>. This means it is an <interfacename>Authentication</interfacename>
|
|
|
+ which is typically an instance of <classname>UsernamePasswordAuthenticationToken</classname> when using username and password based authentication. This can be useful if
|
|
|
+ you need additional information about your user. For example, you might have created a custom <interfacename>UserDetailsService</interfacename>
|
|
|
+ that returns a custom <interfacename>UserDetails</interfacename> containing a first and last name for your user. You could obtain this information with the
|
|
|
+ following:</para>
|
|
|
+ <programlisting language="java"><![CDATA[Authentication auth = httpServletRequest.getUserPrincipal();
|
|
|
+// assume integrated custom UserDetails called MyCustomUserDetails
|
|
|
+// by default, typically instance of UserDetails
|
|
|
+MyCustomUserDetails userDetails = (MyCustomUserDetails) auth.getPrincipal();
|
|
|
+String firstName = userDetails.getFirstName();
|
|
|
+String lastName = userDetails.getLastName();
|
|
|
+ ]]></programlisting>
|
|
|
+ <note>
|
|
|
+ <para>It should be noted that it is typically bad practice to perform so much logic throughout your application. Instead, one should centralize it to reduce
|
|
|
+ any coupling of Spring Security and the Servlet API's.
|
|
|
+ </para>
|
|
|
+ </note>
|
|
|
+ </section>
|
|
|
+ <section xml:id="servletapi-user-in-role">
|
|
|
+ <title>HttpServletRequest.isUserInRole(String)</title>
|
|
|
+ <para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)">HttpServletRequest.isUserInRole(String)</link>
|
|
|
+ will determine if <literal>SecurityContextHolder.getContext().getAuthentication().getAuthorities()</literal> contains a
|
|
|
+ <interfacename>GrantedAuthority</interfacename> with the role passed into <literal>isUserInRole(String)</literal>. Typically users should not pass in the "ROLE_" prefix
|
|
|
+ into this method since it is added automatically. For example, if you want to determine if the current user has the authority "ROLE_ADMIN", you could use the
|
|
|
+ the following:</para>
|
|
|
+ <programlisting language="java"><![CDATA[boolean isAdmin = httpServletRequest.isUserInRole("ADMIN");]]></programlisting>
|
|
|
+ <para>This might be useful to determine if certain UI components should be displayed. For example, you might display admin links only if the current
|
|
|
+ user is an admin.</para>
|
|
|
+ </section>
|
|
|
+ </section>
|
|
|
+ <section xml:id="servletapi-3">
|
|
|
+ <title>Servlet 3+ Integration</title>
|
|
|
+ <para>The following section describes the Servlet 3 methods that Spring Security integrates with.</para>
|
|
|
+ <section xml:id="servletapi-authenticate">
|
|
|
+ <title>HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse)</title>
|
|
|
+ <para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#authenticate%28javax.servlet.http.HttpServletResponse%29">HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse)</link>
|
|
|
+ method can be used to ensure that a user is authenticated. If they are not authenticated, the configured AuthenticationEntryPoint will be used to request the user to authenticate
|
|
|
+ (i.e. redirect to the login page).</para>
|
|
|
+ </section>
|
|
|
+ <section xml:id="servletapi-login">
|
|
|
+ <title>HttpServletRequest.login(String,String)</title>
|
|
|
+ <para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login%28java.lang.String,%20java.lang.String%29">HttpServletRequest.login(String,String)</link>
|
|
|
+ method can be used to authenticate the user with the current <interfacename>AuthenticationManager</interfacename>. For example, the following would attempt to
|
|
|
+ authenticate with the username "user" and password "password":</para>
|
|
|
+ <programlisting language="java"><![CDATA[try {
|
|
|
+ httpServletRequest.login("user","password");
|
|
|
+} catch(ServletException e) {
|
|
|
+ // fail to authenticate
|
|
|
+}]]></programlisting>
|
|
|
+ <note>
|
|
|
+ <para>It is not necessary to catch the ServletException if you want Spring Security to process the failed authentication attempt.</para>
|
|
|
+ </note>
|
|
|
+ </section>
|
|
|
+ <section xml:id="servletapi-logout">
|
|
|
+ <title>HttpServletRequest.logout()</title>
|
|
|
+ <para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout%28%29">HttpServletRequest.logout()</link>
|
|
|
+ method can be used to log the current user out.</para>
|
|
|
+ <para>Typically this means that the SecurityContextHolder will be cleared out, the HttpSession will be invalidated, any "Remember Me" authentication will be
|
|
|
+ cleaned up, etc. However, the configured LogoutHandler implementations will vary depending on your Spring Security configuration. It is important to note
|
|
|
+ that after HttpServletRequest.logout() has been invoked, you are still in charge of writing a response out. Typically this would involve a redirect to the
|
|
|
+ welcome page.</para>
|
|
|
+ </section>
|
|
|
+ <section xml:id="servletapi-start-runnable">
|
|
|
+ <title>AsyncContext.start(Runnable)</title>
|
|
|
+ <para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/AsyncContext.html#start%28java.lang.Runnable%29">AsynchContext.start(Runnable)</link>
|
|
|
+ method that ensures your credentials will be propagated to the new Thread. Using Spring Security's concurrency support, Spring Security overrides
|
|
|
+ the AsyncContext.start(Runnable) to ensure that the current SecurityContext is used when processing the Runnable. For example, the following
|
|
|
+ would output the current user's Authentication:</para>
|
|
|
+ <programlisting language="java"><![CDATA[final AsyncContext async = httpServletRequest.startAsync();
|
|
|
+async.start(new Runnable() {
|
|
|
+ public void run() {
|
|
|
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
|
|
+ try {
|
|
|
+ final HttpServletResponse asyncResponse = (HttpServletResponse) async.getResponse();
|
|
|
+ asyncResponse.setStatus(HttpServletResponse.SC_OK);
|
|
|
+ asyncResponse.getWriter().write(String.valueOf(authentication));
|
|
|
+ async.complete();
|
|
|
+ } catch(Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+});]]></programlisting>
|
|
|
+ </section>
|
|
|
+ <section xml:id="servletapi-async">
|
|
|
+ <title>Async Servlet Support</title>
|
|
|
+ <para>If you are using Java Based configuration, you are ready to go. If you are using XML configuration, there are
|
|
|
+ a few updates that are necessary. The first step is to ensure you have updated your web.xml to use at least the 3.0 schema
|
|
|
+ as shown below:</para>
|
|
|
+ <programlisting language="xml"><![CDATA[<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
|
|
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
|
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
|
|
+ version="3.0">
|
|
|
+
|
|
|
+</web-app>]]></programlisting>
|
|
|
+ <para>Next you need to ensure that your springSecurityFilterChain is setup for processing asynchronous requests.</para>
|
|
|
+ <programlisting language="xml"><![CDATA[<filter>
|
|
|
+ <filter-name>springSecurityFilterChain</filter-name>
|
|
|
+ <filter-class>
|
|
|
+ org.springframework.web.filter.DelegatingFilterProxy
|
|
|
+ </filter-class>
|
|
|
+ <async-supported>true</async-supported>
|
|
|
+</filter>
|
|
|
+<filter-mapping>
|
|
|
+ <filter-name>springSecurityFilterChain</filter-name>
|
|
|
+ <url-pattern>/*</url-pattern>
|
|
|
+ <dispatcher>REQUEST</dispatcher>
|
|
|
+ <dispatcher>ASYNC</dispatcher>
|
|
|
+</filter-mapping>]]></programlisting>
|
|
|
+ <para>That's it! Now Spring Security will ensure that your SecurityContext is propagated on asynchronous requests too.</para>
|
|
|
+ <para>So how does it work? If you are not really interested, feel free to skip the remainder of this section, otherwise read on. 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 with
|
|
|
+ asynchronous requests properly. Prior to Spring Security 3.2, the SecurityContext from the SecurityContextHolder was automatically saved as soon
|
|
|
+ as the HttpServletResponse was committed. This can cause issues in a Async environment. For example, consider the following:</para>
|
|
|
+ <programlisting language="java"><![CDATA[httpServletRequest.startAsync();
|
|
|
+new Thread("AsyncThread") {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ try {
|
|
|
+ // Do work
|
|
|
+ TimeUnit.SECONDS.sleep(1);
|
|
|
+
|
|
|
+ // Write to and commit the httpServletResponse
|
|
|
+ httpServletResponse.getOutputStream().flush();
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}.start();]]></programlisting>
|
|
|
+ <para>The issue is that this Thread is not known to Spring Security, so the SecurityContext is not propagated to it. This means when we commit the
|
|
|
+ HttpServletResponse there is no SecuriytContext. When Spring Security automatically saved the SecurityContext on committing the HttpServletResponse it
|
|
|
+ would lose our logged in user.</para>
|
|
|
+ <para>Since version 3.2, Spring Security is smart enough to no longer automatically save the SecurityContext on commiting the HttpServletResponse
|
|
|
+ as soon as HttpServletRequest.startAsync() is invoked.</para>
|
|
|
+ </section>
|
|
|
+ </section>
|
|
|
+ <section xml:id="servletapi-31">
|
|
|
+ <title>Servlet 3.1+ Integration</title>
|
|
|
+ <para>The following section describes the Servlet 3.1 methods that Spring Security integrates with.</para>
|
|
|
+ <section xml:id="servletapi-authenticate">
|
|
|
+ <title>HttpServletRequest#changeSessionId()</title>
|
|
|
+ <para>The <link xlink:href="http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#changeSessionId()">HttpServletRequest.changeSessionId()</link>
|
|
|
+ is the default method for protecting against <link linkend="ns-session-fixation">Session Fixation</link> attacks in Servlet 3.1 and higher.</para>
|
|
|
+ </section>
|
|
|
+ </section>
|
|
|
+</chapter>
|