2 changed files with 168 additions and 0 deletions
@ -0,0 +1,167 @@
@@ -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> |
||||
Loading…
Reference in new issue