Ver código fonte

SEC-2095: Document Servlet API support

Rob Winch 12 anos atrás
pai
commit
246c632f3a

+ 1 - 0
docs/manual/src/docbook/index.xml

@@ -128,6 +128,7 @@
         </partintro>
         <xi:include href="security-filter-chain.xml"/>
         <xi:include href="core-filters.xml"/>
+        <xi:include href="servlet-api.xml"/>
         <xi:include href="basic-and-digest-auth.xml"/>
         <xi:include href="remember-me-authentication.xml"/>
         <xi:include href="csrf.xml"/>

+ 167 - 0
docs/manual/src/docbook/servlet-api.xml

@@ -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>