Tuesday, August 09, 2011

BHUB: Allow user and password from any URL

The BHUB philosophy is a unique entry point for all the business logic. A Controller makes that happen and there is no question on the big savings. The logic can be used from a CLI script (even pure curl can do the job), it can be of course croned, it can be used from any device (handhelds, desktops, appliances). You get for free every single security you applied in your web tier and I better stop right here because I have written about this several times.

The common way to interact with BHUB would be to get a session, security token from an entry point let us say a login service and from there on keep requesting using that information.

It would be ideal though that the entry point could be any page. In other words allow the first request to be actually not just login but a service request where you provide login information.

In order to accomplish this we will need to hook into Spring once again. Just a custom filter will do the trick. Declare it and assign some permissions for the URLs. In this case I want to allow direct access to certain role (the API role) and I will be opening that functionality to just the pure CLI Controllers which of course does not mean that we cannot allow this functionality for the complete BHUB Service Suite:

<beans:bean id="customLoginFilter" class="com.nestorurquiza.web.filter.CustomLoginFilter"/>
<custom-filter  before="FORM_LOGIN_FILTER" ref="customLoginFilter" /> 
<http auto-config="true" use-expressions="true" access-decision-manager-ref="accessDecisionManager" disable-url-rewriting="true">
  <intercept-url pattern="/cli/**" access="hasRole('ROLE_API')" />


Here is the Filter code.
package com.nestorurquiza.web.filter;

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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
import org.springframework.security.web.util.TextEscapeUtils;

import com.nestorurquiza.utils.CsrfUtil;
import com.nestorurquiza.web.WebConstants;

/**
 * Allowing the creation of a session on the fly when requesting any URL
 * We authenticate the user if not authenticated in the case a user and password is provided 
 * @author nestor
 *
 */
public class CustomLoginFilter implements Filter 
{
    private static final Logger log = LoggerFactory.getLogger(CustomLoginFilter.class);
    
    @Autowired
    UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter;
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 
    {
        HttpServletRequest httpRequest = ((HttpServletRequest) request);
        String uri = httpRequest.getRequestURI();
        if(! uri.contains(".")) {
            HttpServletResponse httpResponse = ((HttpServletResponse) response);
            SecurityContext securityContext = SecurityContextHolder.getContext();
            String method = httpRequest.getMethod();
            String userName = request.getParameter("j_username");
            if(userName != null && "POST".equals(method) && (securityContext == null || securityContext.getAuthentication() == null || !securityContext.getAuthentication().isAuthenticated())) {
                try {
                    Authentication auth = usernamePasswordAuthenticationFilter.attemptAuthentication(httpRequest, httpResponse);
                    securityContext.setAuthentication(auth);
                    SecurityContextHolder.setContext(securityContext);
                    httpRequest.getSession().setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
                    httpRequest.getSession().setAttribute(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(userName));
                    if(auth.isAuthenticated()) {
                        //Initialize CSRF token
                        CsrfUtil.initializeCsrfToken(httpRequest);
                        //Set the token as an attribute in the request to make it pass the security check
                        httpRequest.setAttribute(WebConstants.CSRF_TOKEN, httpRequest.getSession().getAttribute(WebConstants.CSRF_TOKEN));
                    }
                } catch (Exception e){
                    log.info("User could not be authenticated. We go on ...");
                }
                
            }
        }
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() 
    {   

    }

    @Override
    public void init(FilterConfig config) throws ServletException 
    {
    
    }
    
    private Cookie getRememberMeCookie(Cookie[] cookies) {
        if (cookies != null)
          for (Cookie cookie : cookies) {
            if (AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY.equals(cookie.getName())) {
              return cookie;
            }
          }
        return null;
    }
}

No comments:

Followers