How to implement remember password (remember me) for Java web application
- Details
- Written by Nam Ha Minh
- Last Updated on 04 July 2019   |   Print Email
In this Java tutorial, you will learn how to add the remember password (Remember Me) feature to your existing Java web application, to allow the users checks the option “Remember Me” in the login form, so they won’t have to enter username/password in future, until they explicitly logout:
Suppose that you develop your website using Java Servlet, JSP and Hibernate framework. If you use other technologies, the design principle and process are the same.
Table of Content:
1. Understand the Solution to Remember Password Feature
2. Create Database Table and Java Domain Model Class
3. Code DAO Class
4. Update Code of the Login Page
5. Update Code of the Login Servlet
6. Update Code of the Authentication Filter
7. Update Code of the Logout Servlet
8. Test the Remember Password Feature
1. Understand the Solution to Remember Password Feature
There can be different solutions to implement the remember me functionality, but the security may vary. Here we propose a solution which is proven to be safe and secure. Of course we employ cookie mechanism to store some information in the user’s browser but we don’t store user id, username or password straightforward in cookie – that exposes great security risks.
Instead, we store a pair of (selector, validator) in cookie to authenticate the user.
- Selector: is a random String generated when the user logs in with the “Remember Me” option checked. A same value is stored in a database table for the purpose of looking up in the process of auto-login for the user.
- Validator: is a random string associates with the selector. The hashed value is stored in the database. So we compare the hashed value of the validator in the cookie against the value in the database to perform auto-login.
The following picture illustrates the concept of selector-validator:
As you can see, this solution leads to the need of creating an additional table in the database to store pairs of selector-validator. And this table has a foreign key references to the user table in order to look up the associated user. This solution has the following advantages:
- No sensitive information (user id, username and password) is stored in the cookie.
- The random selector eliminates brute-force attacks in which the hackers attempt to try many user ids or usernames.
- The value of validator in the database is hashed using a crypto algorithm like SHA-256. If somehow the authentication table is leak, the hacker cannot generate fake cookies.
- Allow remember password for a user on different browsers and different computers independently.
And the following activity diagram describes the workflow to implement the remember me function when the user/customer logs in:
In case an authentication filter is used, we need to update the workflow as described in the following diagram:
Next, let’s go through the coding steps to implement the Remember Me feature for your website. Note that in the following code, we use the table customer instead of user.
2. Create Database Table and Java Domain Model Class
As per the solution we discussed above, we need to create an authentication table that looks like this:
Here, customer is an existing table and customer_auth is the additional table added to implement the remember me function.
Suppose that we use Hibernate/JPA, so create a new domain model class CustomerAuthToken to map with the customer_auth table.
// copyright www.codejava.net import javax.persistence.*; @Entity @Table(name = "customer_auth") @NamedQueries({ @NamedQuery(name = "CustomerAuthToken.findBySelector", query = "SELECT c FROM CustomerAuthToken c WHERE c.selector = :selector") }) public class CustomerAuthToken implements java.io.Serializable { private Long id; private String selector; private String validator; private Customer customer; public CustomerAuthToken() { } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Long getId() { return id; } @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "customer_id", nullable = false) public Customer getCustomer() { return customer; } // other getters and setters are hidden for brevity }
Note that the customer table has one-to-many relationship with the customer_auth table, so in we also need to update code of the Customer domain model class as follows:
// copyright www.codejava.net import javax.persistence.*; @Entity public class Customer implements java.io.Serializable { private Set<CustomerAuthToken> customerAuthTokens = new HashSet<>(0); // other fields @OneToMany(fetch = FetchType.LAZY, mappedBy = "customer") public Set<CustomerAuthToken> getCustomerAuthTokens() { return customerAuthTokens; } // other setters and getters... }
This allows us to look up a customer associated with a token (selector, validator) like this:
CustomerAuthToken token = … Customer customer = token.getCustomer();
We refer a pair of (selector, validator) as a token.
3. Code DAO Class
Next, code the CustomerAuthDAO class just like a typical Hibernate/JPA DAO class with an additional method that finds a CustomerAuthToken by selector:
// copyright www.codejava.net public class CustomerAuthDAO extends JpaDAO<CustomerAuthToken> implements GenericDAO<CustomerAuthToken> { public CustomerAuthToken findBySelector(String selector) { List<CustomerAuthToken> list = super.findWithNamedQuery( "CustomerAuthToken.findBySelector", "selector", selector); if (!list.isEmpty()) { return list.get(0); } return null; } }
The findBySelector() method will be used to find a token in the authentication table, given a selector read from the cookie.
Learn more about JPA/Hibernate here: Java Hibernate JPA Annotations Tutorial for Beginners
4. Update Code of the Login Page
In the login JSP page, add a checkbox to present the “Remember Me” option:
<input type="checkbox" name="rememberMe" value="true">Remember Me
The login form should look like this:
5. Update Code of the Login Servlet
Suppose that you have a Java Servlet class to handle the login submission request. So we need to read the value of the remember option, and if the user/customer has logged in successfully, create a new token and save it into the database, and then create cookies to store the pair of selector-validator.
The workflow of the code should look like this:
// copyright www.codejava.net public void doLogin() throws ServletException, IOException { String email = request.getParameter("email"); String password = request.getParameter("password"); boolean rememberMe = "true".equals(request.getParameter("rememberMe")); Customer customer = customerDAO.checkLogin(email, password); if (customer == null) { // login failed, show login form again with error messsage } else { // login succeed, store customer information in the session HttpSession session = request.getSession(); session.setAttribute("loggedCustomer", customer); if (rememberMe) { // create new token (selector, validator) // save the token into the database // create a cookie to store the selector // create a cookie to store the validator } // show destination page } }
Here’s the code to create a new token:
// copyright www.codejava.net CustomerAuthToken newToken = new CustomerAuthToken(); String selector = RandomStringUtils.randomAlphanumeric(12); String rawValidator = RandomStringUtils.randomAlphanumeric(64); String hashedValidator = HashGenerator.generateSHA256(rawValidator); newToken.setSelector(selector); newToken.setValidator(hashedValidator); newToken.setCustomer(customer);
We use the RandomStringUtils class from Apache Commons Lang library, so make sure you add the following dependency to your project:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.8.1</version> </dependency>
And HashGeneratoris a utility class. Read this tutorial to create it: How to calculate MD5 and SHA hash values in Java
Notice we set a Customer object for the token:
newToken.setCustomer(customer);
This Customer object is the result of the check login process:
Customer customer = customerDAO.checkLogin(email, password);
And save the new token into the database:
CustomerAuthDAO authDao = new CustomerAuthDAO(); authDao.create(newToken);
And here’s the code to create two cookies to store the values of selector and validator:
Cookie cookieSelector = new Cookie("selector", selector); cookieSelector.setMaxAge(604800); Cookie cookieValidator = new Cookie("validator", rawValidator); cookieValidator.setMaxAge(604800); response.addCookie(cookieSelector); response.addCookie(cookieValidator);
Note that we set the max age of the cookies to 604,800 seconds = 7 days so they are stored permanently in the browser for that time, even when the user exits the browser. You can change this value depending on your need.
6. Update Code of the Authentication Filter
Typically, the workflow of an authentication filter looks like this:
// copyright www.codejava.net public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // check user's login status in the session if (user is not logged in && current page requires authentication) { // forward to the login page } else { // user is logged in, move forward chain.doFilter(request, response); } }
To implement the remember password feature, the workflow should be updated like this:
// copyright www.codejava.net public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // check user's login status in the session // read cookie if (user is not logged in && has cookie) { // read (selector, validator) from cookies // find a token by the selector in the database if (token is found) { // read the hashed value of validator from the token -> v1 // hash the validator from the database -> v2 if (v1 equals v2) { // user is authenticated, set login status in session // update new token in the database // update cookies } } } if (user is not logged in && current page requires authentication) { // forward to the login page } else { // user is logged in, move forward chain.doFilter(request, response); } }
And here’s the example code to implement remember password feature in the authentication filter:
// copyright www.codejava.net HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; HttpSession session = httpRequest.getSession(false); boolean loggedIn = session != null && session.getAttribute("loggedCustomer") != null; Cookie[] cookies = httpRequest.getCookies(); if (!loggedIn && cookies != null) { // process auto login for remember me feature String selector = ""; String rawValidator = ""; for (Cookie aCookie : cookies) { if (aCookie.getName().equals("selector")) { selector = aCookie.getValue(); } else if (aCookie.getName().equals("validator")) { rawValidator = aCookie.getValue(); } } if (!"".equals(selector) && !"".equals(rawValidator)) { CustomerAuthDAO authDAO = new CustomerAuthDAO(); CustomerAuthToken token = authDAO.findBySelector(selector); if (token != null) { String hashedValidatorDatabase = token.getValidator(); String hashedValidatorCookie = HashGenerator.generateSHA256(rawValidator); if (hashedValidatorCookie.equals(hashedValidatorDatabase)) { session = httpRequest.getSession(); session.setAttribute("loggedCustomer", token.getCustomer()); loggedIn = true; // update new token in database String newSelector = RandomStringUtils.randomAlphanumeric(12); String newRawValidator = RandomStringUtils.randomAlphanumeric(64); String newHashedValidator = HashGenerator.generateSHA256(newRawValidator); token.setSelector(newSelector); token.setValidator(newHashedValidator); authDAO.update(token); // update cookie Cookie cookieSelector = new Cookie("selector", newSelector); cookieSelector.setMaxAge(604800); Cookie cookieValidator = new Cookie("validator", newRawValidator); cookieValidator.setMaxAge(604800); httpResponse.addCookie(cookieSelector); httpResponse.addCookie(cookieValidator); } } } }
Note that after the user has been automatically authenticated, we create new values for selector and validator in order to tighten security.
Learn more about authentication filter: How to implement authentication filter for Java web application
7. Update Code of the Logout Servlet
Finally, if the user explicitly logs out from the website, we need to delete the cookies and token from the database. Here’s the example code in a Java servlet that handles logout request:
// copyright www.codejava.net protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().removeAttribute("loggedCustomer"); Cookie[] cookies = request.getCookies(); if (cookies != null) { String selector = ""; for (Cookie aCookie : cookies) { if (aCookie.getName().equals("selector")) { selector = aCookie.getValue(); } } if (!selector.isEmpty()) { // delete token from database CustomerAuthDAO authDao = new CustomerAuthDAO(); CustomerAuthToken token = authDao.findBySelector(selector); if (token != null) { authDao.delete(token.getId()); Cookie cookieSelector = new Cookie("selector", ""); cookieSelector.setMaxAge(0); Cookie cookieValidator = new Cookie("validator", ""); cookieValidator.setMaxAge(0); response.addCookie(cookieSelector); response.addCookie(cookieValidator); } } } response.sendRedirect(request.getContextPath()); }
Note that in the above code, we set the max age of the cookies to 0 to remove them.
8. Test the Remember Password Feature
When testing, check the data inserted into the authentication table:
And also check cookies in the browser:
If you test the website on localhost with Chrome, type the following URL to see the cookies:
chrome://settings/cookies/detail?site=localhost
Remember to test with different users, different browsers and different computers.
That’s my step-by-step guide to implement the remember me/remember password feature for an existing Java web application. For learning to develop full website, I recommend you to take the Java Servlet, JSP and Hibernate: Build a Complete Website course.
Other Java Coding Tutorials:
- How to display images from database in JSP page with Java Servlet
- How to implement forgot password feature for Java web application
- How to code login and logout with Java Servlet, JSP and MySQL
- How to Code Hit Counter for Java web application
- 10 Common Mistakes Every Beginner Java Programmer Makes
- 10 Java Core Best Practices Every Java Programmer Should Know
- How to become a good programmer? 13 tasks you should practice now
- How to calculate MD5 and SHA hash values in Java
- Java File Encryption and Decryption Example
Comments
I choose Set which does not allow duplicate elements.
I wonder what happens if there are two same selectors in the database (it is possible because the selector is generated randomly), in the method .findbyselector, you take the first one, but it is not true if the current user is associated with the second selector. I don't see the unique key of attribute selector in the table customer_auth. Many thanks to your tutorials.
Simply delete all rows from that able. Then all remembered users are required to login again.
No solution to fix. If that case happens, meaning that you have permission on that computer, or you are able to hack the browser.
If I have 2 keys and you get one, you are able to open my house's door just like me.