Thursday, August 4, 2011

Integrating OpenId into my Struts 2 app - Part 3 - The LoginAction Action class

The LoginAction class controls the flow between the user's authentication request and the business bean that does our heavy lifting.  Aside from a Logout method that removes the user's http session, the two main methods in this class are as follows:
  • validateOpenId() - This method is called after the user selects an OpenID provider from the OpenID Selector component on the Login form.  The Action then calls the business bean (OpenIdAuthenticator) which generates the destination URL for OpenId Provider.  The request is then redirected to the destination URL.  It's important to note that we are now redirecting the user away from our web application.  Our application never sees or has access to the user's password.  The OpenID Provider will let us know if the use has successfully authenticated.
  •  validateOpenId() - Once the user has successfully authenticated with the OpenID Provider.  The Provider redirects the user back to our web application, using the return URL we provided.  This method processes that request.  Again we pass it off to the business bean and let it confirm the login was successful and glean any relevant information sent by the OpenID Provider.  The business bean will return a User object which we'll persist in the database and add to the user's HTTP Session.  At this point we'll redirect the user to the resource he was originally requesting before we forced him to authenticate.
Here's the Action class:
public class LoginAction extends FlashCardsAppBaseAction implements Preparable, ServletRequestAware, ServletResponseAware, SessionAware, ApplicationAware {

    static Logger logger = Logger.getLogger(LoginAction.class);

    // this is the only form field we will be looking for from OpenID Selector on the front end
    private String openid_identifier;

    // Hibernate Session
    private Session hibernateSession;
    
    // we'll need access to the Servlet spec objects, rather than just their attribute or parm maps
    private HttpServletRequest request;
    private HttpServletResponse response;
    
    // we'll be storing the User object in the Session
    private Map<String, Object> httpSession;
    
    // the OpenIdAuthenticator class needs access to the application to store a OpenId4Java related object
    private Map<String, Object> application;
    
    // we'll need to send this to the OpenId provider so it knows where to send its response 
    private final String returnAction = "/home/authenticateOpenId.action";

    // the OpenID Selector form will submit to this Action method
    public String validateOpenId() throws Exception {

        logger.debug("Entering validateOpenId()");
        
        // get rid of trailing slash
        if (getOpenid_identifier().endsWith("/")) {
            setOpenid_identifier(getOpenid_identifier().substring(0, getOpenid_identifier().length() - 1));
        }

        logger.debug("The requested OpenId identifier is: " + getOpenid_identifier());

        // determine a return_to URL where the application will receive
        // the authentication responses from the OpenID provider
        String returnUrl = getServerContext(request) + returnAction;
        
        // construct the destination Url to send to the Open Id provider
        String destinationUrl = OpenIdAuthenticator.getValidateOpenIdUrl(returnUrl, this.getOpenid_identifier(), httpSession, application); 
        
        // redirect to the Auth Request
        response.sendRedirect(destinationUrl);
        
        // no need to return a view
        return NONE;
    }
    
    public String authenticateOpenId() throws Exception {
        logger.debug("Entering authenticateOpenId()");

        Map<String,String[]> parmList = request.getParameterMap();

        // extract the receiving URL from the HTTP request
        final StringBuffer receivingURL = request.getRequestURL();
        final String queryString = request.getQueryString();

        if (queryString != null && queryString.length() > 0) {
            receivingURL.append("?").append(request.getQueryString());
        }
        
        logger.debug(receivingURL.toString().replaceAll("&", "\n"));

        // verify the user has authenticated with the Open Id provider and 
        // get a reference to the authenticated User
        User user = OpenIdAuthenticator.getAuthenticatedUser(parmList, receivingURL, httpSession, application);

        // save the user to the DB
        UserPersister uPersister = new UserPersister();
        uPersister.saveOrUpdateUser(user, hibernateSession);
        
        // add the user to the HTTP Session
        httpSession.put("user", user);
        
        // retrieve the original URL from the Session
        String desitinationURL = (String)httpSession.get("originalURL");

        // was a destination URL provided?
        if (desitinationURL == null) {
            logger.debug("No destination URL provided, will send to Home");
            return "home";
        }
        else {
            logger.debug("Redirecting to : " + desitinationURL);
            response.sendRedirect(desitinationURL);
            return NONE;
        }
    }

    @SuppressWarnings("rawtypes")
    public String logout() {
        logger.debug("Entering logout()");
        
        try {
            // invalidate the user's session
            httpSession.remove("user");

            if (httpSession instanceof org.apache.struts2.dispatcher.SessionMap) {
                try {
                    ((org.apache.struts2.dispatcher.SessionMap) httpSession).invalidate();
                } catch (IllegalStateException e) {
                    logger.error("Exception in logout()", e);
                }
            }
            
            return "success";
        } catch (Exception e) {
            logger.error("Exception in logout():", e);
            
            return "error";
        }
    }

    private String getServerContext(final HttpServletRequest request) {
        // Get the base url.
        final StringBuilder serverPath = new StringBuilder();
        
        serverPath.append(request.getScheme() + "://");
        serverPath.append(request.getServerName());

        if (request.getServerPort() != 80) {
            serverPath.append(":" + request.getServerPort());
        }
        serverPath.append(request.getContextPath());
        
        return serverPath.toString();
    }
    
    @Override
    public void prepare() throws Exception {
        logger.debug("Entering prepare()");

        hibernateSession = getHibernateSession();
    }

    public String getOpenid_identifier() {
        return openid_identifier;
    }

    public void setOpenid_identifier(String openid_identifier) {
        this.openid_identifier = openid_identifier;
    }

    @Override
    public void setSession(Map<String, Object> httpSession) {
        this.httpSession = httpSession;
    }
    
    @Override
    public void setServletResponse(final HttpServletResponse response) {
        this.response = response;
    }

    @Override
    public void setServletRequest(final HttpServletRequest request) {
        this.request = request;
    }

    @Override
    public void setApplication(Map<String, Object> application) {
        this.application = application;
    }
}

In the final post in this series, I'll include the code for the OpenIdAuthenticator business bean which does the heavy lifting and relies heavily on the OpenId4Java library.

No comments:

Post a Comment