I am using Form-based authentication provided by GlassFish v3 btw.
Most of the website, when the user about to provide the username
and password
to log into the system, it has a checkbox “Stay logged in”, that if you check the box, if will always let you stay logged. How do I achieve that?
Use a long-living cookie to track the unique client and use the new Servlet 3.0 API provided programmatic login HttpServletRequest#login()
when the user is not logged-in but the cookie is present.
This is the easiest to achieve if you manage the users in a database table which is provided to Glassfish by JDBCRealm and is the same table as where your User entity comes from. You could then create another table with a java.util.UUID
value as PK and the ID of the user in question as FK.
Assume the following login form:
<form action="login" method="post">
<input type="text" name="username" />
<input type="password" name="password" />
<input type="checkbox" name="remember" value="true" />
<input type="submit" />
</form>
And the following in doPost()
method of a Servlet which is mapped on /login
:
String username = request.getParameter("username");
String password = hash(request.getParameter("password"));
boolean remember = "true".equals(request.getParameter("remember"));
User user = userDAO.find(username, password);
if (user != null) {
request.login(user.getUsername(), user.getPassword()); // Password should already be the hashed variant.
request.getSession().setAttribute("user", user);
if (remember) {
String uuid = UUID.randomUUID().toString();
rememberDAO.save(uuid, user);
addCookie(response, COOKIE_NAME, uuid, COOKIE_AGE);
} else {
rememberDAO.delete(user);
removeCookie(response, COOKIE_NAME);
}
}
(the COOKIE_NAME
should be the unique cookie name, e.g. “remember” and the COOKIE_AGE
should be the age in seconds, e.g. 2592000 for 30 days)
Here’s how the doFilter()
method of a Filter which is mapped on restricted pages could look like:
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
User user = request.getSession().getAttribute("user");
if (user == null) {
String uuid = getCookieValue(request, COOKIE_NAME);
if (uuid != null) {
user = rememberDAO.find(uuid);
if (user != null) {
request.login(user.getUsername(), user.getPassword());
request.getSession().setAttribute("user", user); // Login.
addCookie(response, COOKIE_NAME, uuid, COOKIE_AGE); // Extends age.
} else {
removeCookie(response, COOKIE_NAME);
}
}
}
if (user == null) {
response.sendRedirect("login");
} else {
chain.doFilter(req, res);
}
In combination with those cookie helper methods (too bad they are missing in Servlet API):
public static String getCookieValue(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (name.equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
Cookie cookie = new Cookie(name, value);
cookie.setPath("/");
cookie.setMaxAge(maxAge);
response.addCookie(cookie);
}
public static void removeCookie(HttpServletResponse response, String name) {
addCookie(response, name, null, 0);
}
Although the UUID is extremely hard to brute-force, you could provide the user an option to lock the “remember” option to user’s IP address (request.getRemoteAddr()
) and store/compare it in the database as well. This makes it a tad more robust.
It’s also a good practice to replace the UUID value whenever the user has changed its password.