How to program using cookies with Java and Spring MVC

Cookies allow to save information to improve the user navegation and are also used by many external tools like Google Analytics. Let’s see how to use them with Java, Spring MVC and Security, and how to prevent legal problems.

Sample code to create cookies

The Java code is simple and can be used by a Spring’s controller class. To create a cookie you only need to create a Cookie object, set its name and value, and add it to the response. It’s good to also set how long it will be available (maxAge). This is specified in seconds so a month would be 2592000 (30 days x 24 hours x 60 minutes x 60 seconds). I recommend to write it in a properties file so it will be easy to change the value without modifying the Java code.

public class CookieHelper{
  /**
  * Adds a cookie to the web browser
  * */
  public static void saveCookie(String cookieName, String value, int maxAge, HttpServletResponse response) {
    Cookie cookie = new Cookie(cookieName, value);
    cookie.setMaxAge(maxAge);
    response.addCookie(cookie);
  }

How to check that a cookie has been created successfully

Every web browser has an option to show and delete the loaded cookies. In Firefox you can do this clicking on Options → Privacy tab → Show cookies. If you are deploying on your computer the cookie will appear at the localhost group, and if it is on a server it will appear at the group related to the url.

Sample code to read the values of the cookies

The cookies are loaded on the web browser and you can iterate over them to obtain the value of a desired one:

  /**
  * Reads a cookie from the web browser
  * */
  public static String getCookieValue(String cookieName, HttpServletRequest request) {
    String value = null;
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
      int i = 0;
      boolean cookieExists = false;
      while (!cookieExists && i < cookies.length) {
        if (cookies[i].getName().equals(cookieName)) {
          cookieExists = true;
          value = cookies[i].getValue();
        } else {
          i++;
        }
      }
    }
    return value;
  }
}

Adding cookies during the Spring Security login 

The forms that use Spring Security usually have two input fields,  j_username and j_password, and send their values via a POST petition to  j_spring_security_check after encrypting the password with an algorithm like SHA1. If you want to send an extra param you can add it to the form but, how do you read it in Java?

You can do it in a class that handles the successfull login events. It has to implement AuthenticationSuccessHandler and extends a Spring’s class that handles de logins like SavedRequestAwareAuthenticationSuccessHandler.

Notice that the parameter map of the request is not an instance of Map<String,String>, but of Map<String,String[]> , because every param can appear more than once (for example, if the form has a multiple select). The cookie must be added to the response BEFORE doing a redirect and before calling super.onAuthenticationSuccess(…).

public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler implements
AuthenticationSuccessHandler {

  public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    // save a cookie with the selected language
    // this must be done BEFORE the sendRedirect
    Map<String, String[]> parameterMap = request.getParameterMap();
    if (parameterMap.containsKey("language")) {
      saveLanguageCookie(parameterMap.get("language")[0], response);
    }
    ...
    super.onAuthenticationSuccess(request, response, authentication);
  }

  private void saveLanguageCookie(String language, HttpServletResponse response) {
    // obtain the maximum time that a cookie will be available
    String maxAgeValueText = messageService.getValue("cookieMaxAge").trim()
    int maxAge = Integer.parseInt(maxAgeValueText);

    // save a cookie with the value of the selected language
    String cookieName = messageService.getValue("languageCookieName").trim();
    CookieHelper.saveCookie(cookieName, language, maxAge, response);
  }
}

This class should be added to the settings of Spring Security by registering a bean that will handle the authentication events.

<security:http ...>
  <security:form-login ... authentication-success-handler-ref="customAuthenticationSuccessHandler"/>
  ...
</security:http>

<bean id="customAuthenticationSuccessHandler" class="com.rafaborrego.security.CustomAuthenticationSuccessHandler">
  ...
</bean>

Adding a legal warning to the website

The use of cookies is not allowed in many countries without user consent, so many websites display a message in a div asking for permission, and they create a cookie when the user has accepted in order to not display again the message.

This is an example of doing it with JSP:

<c:if test="${!hasAcceptedCookies}">
  <div id="cookieArea">
    <!-- summarized legal warning -->
    <!-- link to the full legal text -->
    <!-- confirm button -->
  </div>
</c:if>

The confirm button would open an address that would save the cookie. For example:

@Controller
public class CookieController extends GenericController {

  @RequestMapping(value = {"acceptsCookies.do", PRIVATE + "acceptsCookies.do"}, method = RequestMethod.GET)
  public String acceptsCookies(HttpServletResponse response) {
    
    //obtain the maximum time that a cookie will be available in the web browser
    String maxAgeText = messageService.getValue("cookieMaxAge").trim();
    int maxAge = Integer.parseInt(maxAgeText);

    //obtain the cookie name from the properties file and creates the cookie
    String cookieName = messageService.getValue("userAcceptsCookiesCookieName").trim();
    CookieHelper.saveCookie(cookieName, "YES", maxAge, response);

    return "redirect:...";
  }
}

The other controllers can check if this cookie has been created using a ModelAttribute:

@ModelAttribute("hasAcceptedCookies")
public boolean hasAcceptedCookies(HttpServletRequest request) {
  String cookieName = messageService.getValue("userAcceptsCookiesCookieName").trim();
  boolean hasAcceptedCookies = existsCookie(cookieName, request);

  return hasAcceptedCookies;
}

Using external tools that create cookies

There are many tools that do this like Google Analytics and we can receive legal claims if we allow them to create cookies without user consent. So we should specify them in the full legal text of the website and use a code similar to the previous one to prevent loading them until the user has accepted their use.

Disclaimer: the use of legal warnings described above may not be valid for every kind of web and for every country, so I won’t be liable for any claim or damage related to its use. If you use it, it will be at your own risk.

 

Rafael Borrego

Consultant and security champion specialised in Java, with experience in architecture and team management in both startups and big corporations.

Disclaimer: the posts are based on my own experience and may not reflect the views of my current or any previous employer

Facebook Twitter LinkedIn 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>