Recipe11.7.Allowing a User to Log in Automatically


Recipe 11.7. Allowing a User to Log in Automatically

Problem

You want to allow users to be logged in automatically if they have valid credentials stored in a cookie(s).

Solution

Use a servlet filter, such as the one shown in Example 11-12, that looks for cookies containing the user's credentials. The credentials are used to authenticate the user. If the authentication succeeds, the user is automatically logged in; otherwise, the user will be prompted to login.

Example 11-12. Cookie authentication filter for automatic login
package com.oreilly.strutsckbk.ch11; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /**  * Filter which handles application authentication.  The filter implements  * the following policy:  * <ol>  * <li>If the username is in the session the filter exits;  * <li>If not, the authentication cookies are looked for;  * <li>If found, the authentication is attempted  * <li>If authentication is successful, the username is stored in   * the session  * <li>Otherwise, the cookies are invalid and subsequently removed   * from the response  * </ol>  *   * @author Bill Siggelkow  */ public class AutomaticLoginFilter implements Filter {     private String onFailure = "logon.jsp";     public void init(FilterConfig filterConfig) throws ServletException {         this.filterConfig = filterConfig;         onFailure = filterConfig.getInitParameter("onFailure");     }         public void doFilter(ServletRequest request, ServletResponse response,                           FilterChain chain)                   throws IOException, ServletException {         HttpServletRequest req = (HttpServletRequest) request;         HttpServletResponse res = (HttpServletResponse) response;                  String contextPath = req.getContextPath( );         // if the requested page is the onFailure page continue         // down the chain to avoid an infinite redirect loop                 if (req.getServletPath( ).equals(onFailure)) {             chain.doFilter(request, response);             return;         }                  // get the session or create it           HttpSession session = req.getSession( );          String username = (String) session.getAttribute("username");         if (log.isDebugEnabled( )) log.debug("User in session:"+username);         // if user is null get credentials from cookie; otherwise continue         if (username == null) {             boolean authentic = false;             username = findCookie(req, "StrutsCookbookUsername");             String password = findCookie(req, "StrutsCookbookPassword");             if (username != null && password != null) {                 try {                     if (log.isDebugEnabled( )) log.debug("Checking                                                           authentication");                     // Call your security service here                     //SecurityService.authenticate(username, password);                     session.setAttribute("username", username);                     authentic = true;                 }                 catch (Exception e) {                     log.error("Unexpected authentication failure.", e);                     clearCookie(res, "StrutsCookbookUsername");                     clearCookie(res, "StrutsCookbookPassword");                 }             }                  // if not authentic redirect to the logon page             if (!authentic) {                 //redirect to the onFailure page, alternatively we could send                 //an HTTP error code such as 403 (Forbidden)                 res.sendRedirect(contextPath+onFailure);                 //abort filter instead of chaining                 return;             }         }         if (log.isDebugEnabled( )) log.debug("Continuing filter chain ...");         chain.doFilter(request, response);     }          public void destroy( ) {         // Nothing necessary     }     private String findCookie(HttpServletRequest request, String cookieName)     {         Cookie[] cookies = request.getCookies( );         String value = null;         if (cookies != null) {             for (int i=0; i<cookies.length; i++) {                 if (cookies[i].getName( ).equals(cookieName)) {                     value = cookies[i].getValue( );                 }             }         }         return value;     }     private void clearCookie(HttpServletResponse response, String cookieName)     {         // the cookie value does not matter         Cookie cookie = new Cookie(cookieName, "expired");         // setting maxAge to 0 effectively removes the cookie         cookie.setMaxAge(0);         response.addCookie(cookie);     }     private FilterConfig filterConfig;     private static final Log log = LogFactory.getLog(AutomaticLoginFilter.                                                      class); }

Discussion

This Solution assumes that cookies for the username and password have been stored in the request; this filter does not store the cookies.

For that functionality, you need to use an Action such as the one shown in Recipe 11.5.


You map the servlet filter shown in the Solution to any application URLs requiring user authentication. If users aren't authenticated, they should be redirected to the page specified by the onFailure initialization parameter (defaults to logon.jsp). You describe the filter's configuration using the filter and filter-mapping elements in the web.xml file, as shown in Example 11-13.

Example 11-13. Filter deployment settings (partial)
<filter>     <filter-name>AutomaticLoginFilter</filter-name>     <filter-class>         com.oreilly.strutsckbk.ch11.AutomaticLoginFilter     </filter-class>     <init-param>         <param-name>onFailure</param-name>         <param-value>/my_logon.jsp</param-value>     </init-param> </filter> <filter-mapping>     <filter-name>AutomaticLoginFilter</filter-name>     <url-pattern>/reg/*</url-pattern>   </filter-mapping> <filter-mapping>     <filter-name>AutomaticLoginFilter</filter-name>     <url-pattern>/admin/menu.do</url-pattern>   </filter-mapping>

When a request is received, the servlet filter attempts to retrieve specific cookie values for the username and password. If the values aren't present, control will be redirected to the onFailure page. If the cookies are present, the username and password will be verified using a SecurityService. If authentic, the request is passed to the next filter in the chain, effectively allowing the request to proceed as normal. If not authentic, the cookie values themselves are invalid and not legitimate. The cookies are removed and control is redirected to the onFailure page.

From a developer's perspective, one of the more interesting aspects of this filter is the following bit of code:

if (req.getServletPath( ).equals(onFailure)) {     chain.doFilter(request, response);     return; }

When this filter was first written (by yours truly), this block was omitted. The filter was mapped to all application URLs (/) and the application was deployed. When an attempt was made to access to any part of the application, a browser message was displayed indicating too many redirects had been attempted. What happened was that when the filter redirected to the logon page, the request was routed back through the filter, essentially creating an HTTP infinite loop. To fix this problem, the code block was added to skip the authentication check if the request path is the same as the onFailure path.

See Also

Recipe 11.10 shows how to use the open source SecurityFilter software for providing similar functionality as the filter presented in this recipe.

Java Servlet Programming by Jason Hunter (O'Reilly) covers Servlet development in-depth, including filters and the cookie-related APIs.



    Jakarta Struts Cookbook
    Jakarta Struts Cookbook
    ISBN: 059600771X
    EAN: 2147483647
    Year: 2005
    Pages: 200

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net