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.