Posts Tagged ‘Java’

Login Tokens with Java Servlets

If you don’t want to read the whole thing, here is the short version: I wrote a servlet filter to log a user in from a token cookie that does not contain a user name and password. It is released under the GPLv3 license. It’s on github. Please use it, please test it, please send feedback and bug fixes.

Let’s talk about user authentication in web apps. When a web app keeps track of my data, whether it’s bookmarks, book orders or bank data, I expect to log in with a reasonably secured username and password. If I’m authenticated the web app sets up a cookie to track my session. There are two parts to a session, the client side – a cookie is placed in my browser with a session id, and the server side – the app server uses a map with my user data keyed by my session id stored in the cookie sent back to me.

Once my browser has a session cookie it sends that cookie back to the web app with every HTTP request I make. The app server uses the session id in that cookie to load my user data and, say, add a book to my shopping cart. My user data is updated in the session and the web page I requested is sent back to the browser. The next time I make a request for a web page the session cookie is sent back to the web app and again my user data is loaded from the session map. Though this approach violates a strict RESTful architecture it’s the best we’ve got for now.

As long as I keep requesting web pages while my session is active I keep delaying the session timeout on the app server. A session typically times out on an app server due to the cost of keeping all user data in memory in a session map. With limited memory we have to timeout inactive user sessions so that the app server can serve active users more effectively (i.e. without crashing). If I wait 5 minutes between web requests I’m likely to find my bank session timed out and I have to log in again. When ordering books that timeout period may be 30 minutes or longer. It seems like the more valuable the data, the shorter the session timeout. This is to prevent another user from coming in and reusing a session tracking cookie for “bad things” like transferring all my money to his account.

This brings up the point that though I still have a session id in my browser, the session on the server side no longer exists. What if I wasn’t done shopping for books and computer parts? I have to log in again and start a new session – my browser gets a new cookie with a new session id and the app server starts a new session keyed by my session id. My user data, hopefully my up-to-date shopping cart, is loaded into my new session and I may continue shopping with another 30 minute or so timeout. If I keep requesting web pages my session timeout keeps moving to expire 30 minutes after my last page request. I say 30 minutes but it could be any arbitrary period.

Expiring user sessions is a good practice because it forces users to authenticate themselves every so often. One user doesn’t always use one browser and one browser doesn’t always serve one person. But what if that statement is close enough to true that it might as well be? What if I’ve got 5 browsers where I’m the only user? Sometimes I don’t want to be required to use the login form every time I use a web app.

GMail can remember my login for two weeks. Yahoo! Mail can, too. I don’t have to log in every time I got to those web apps. I wanted the same functionality for a Java-based web app I wrote and I couldn’t find anything to do something similar through the search engines. The Java packages I found stored the user’s login information in the cookie in plain text. The next time the user goes to the web app the form data is auto-populated by the plain-text username and password stored in the cookie. This is a very bad “security” practice and the user *still* needs to visit a login page.

Rather than storing a username and password in a cookie we can do a little better. We can store a “token” in the cookie that is associated with the user. This token does not store the user’s username or password, either in plain-text or encrypted. Instead the token is generated by the web app and placed in a cookie that does not track the user’s session. This means the web app sends one more cookie, the login token, in addition to the session tracking cookie.

Any time after a user is authenticated we can set the login token cookie. This could be as part of the login process if a login form has a “remember me” check box or at the user’s explicit request any time after authentication. How does this work in practice? Let’s look at some code. First we’ll look at the TokenService class.

@Service
public class TokenService {

  @PersistenceUnit(unitName="bookmarksPU")
  EntityManagerFactory emf;

  public String setupNewLoginToken(User user) {
    PersistentLoginToken tonaken = new PersistentLoginToken();
    EntityManager em = emf.createEntityManager();

    em.getTransaction().begin();
    User u = em.find(User.class, user.getId());
    token.setUser(u);
    em.persist(token);
    em.getTransaction().commit();

    return token.getId();
  }

  public User loginWithToken(String tokenId) {
    EntityManager em = emf.createEntityManager();
    em.getTransaction().begin();
    PersistentLoginToken token = (PersistentLoginToken) em.find(PersistentLoginToken.class, tokenId);

    User user = null;
    if (token != null) {
      user = token.getUser();
      em.remove(token);
      em.getTransaction().commit();
    } else {
      em.getTransaction().rollback();
      //TODO this is a forgery attempt
      throw new RuntimeException("Attempted login token cookie forgery");
    }
    return user;
  }
}

TokenService#setupNewLoginToken is called when we have a User object – sometime after authentication or when the user requests it. TokenService#loginWithToken is called in a servlet filter before an HTTP request even makes it to our code. In order for this to work we need to tie a login token to a user. We do that through PersistentLoginToken.

@Entity
@Table(name="persistentLoginTokens")
public class PersistentLoginToken {
  @Id
  @GeneratedValue(strategy=GenerationType.AUTO, generator="uuid-hex")
  private String id;

  @ManyToOne
  private User user;

  public void setUser(User user) {
    this.user = user;
  }

  public User getUser() {
    return user;
  }

  public void setId(String id) {
    this.id = id;
  }

  public String getId() {
    return id;
  }
}

The table required (or generated) by this entity has an id column and a user_id column. We have our JPA provider generate a 128-bit UUID to serve as the token id. The user id is obtained from the user associated with this token. Notice how we do not constrain the number of valid tokens a user can have at any particular time. This means I can have valid login tokens across 5, 10 or 100+ browsers. Each browser would get a new unique token when I log in using that browser.

Back to the setupNewLoginToken method – this works by creating a new PersistentLoginToken object and associating it with a user. When it PersistentLoginToken object is saved the JPA provider generates its UUID and makes an entry in the database. Now that the PersistentLoginToken has a generated UUID we put that UUID into the loginToken cookie and add that cookie to the HttpResponse object. We set the cookie’s expiration time to 1 week, though this can be set to whatever your web app requires.

This login token cookie is sent back to the browser at the end of this response. The browser then sends this loginToken cookie to the web app on every subsequent request. Though this is useless when I have an active session, I really want this cookie around when my session expires on the server side or my session tracking cookie expires on the browser side. Let’s see what happens when I don’t have an active session and I visit the web app.

@Component
public class PersistentLoginFilter implements Filter {

  @PersistenceUnit(unitName="webappPU")
  EntityManagerFactory emf;

  @Override
  public void doFilter(ServletRequest request,
                       ServletResponse response,
                       FilterChain chain) throws IOException, ServletException {

    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;
    Cookie tokenCookie = getCookieByName(httpRequest.getCookies(), "loginToken");

    HttpSession session = httpRequest.getSession();
    User user = (User) session.getAttribute("user");

    if (user == null && tokenCookie != null) {
      user = tokenService.loginWithToken(tokenCookie.getValue());
      String tokenValue = tokenService.setupNewLoginToken(user);

      httpRequest.getSession().setAttribute("user", user);
      tokenCookie.setMaxAge(0);
      httpResponse.addCookie(tokenCookie);

      tokenCookie = new Cookie("loginToken", tokenValue);
      tokenCookie.setPath("/bookmarks");
      tokenCookie.setMaxAge(168 * 60 * 60);
      httpResponse.addCookie(tokenCookie);
    }

    chain.doFilter(httpRequest, httpResponse);
  }
// other methods omitted
}

The doFilter method looks for a User associated with the current web session. The absence of a User and the presence of a login token cookie indicate it’s time to perform a user lookup and login based on the token cookie. After finding the user based on a valid token the filter gives the user a new login token that is valid for another week. This may or may not be ok for your web app. I just didn’t feel like writing the code to set expiration of the new cookie to the remaining time on the original cookie.

With our User in hand, we set it on the current session and let the request finish processing. All this code is available at http://github.com/anthonychaves/bookmarks under the GPLv3 license. Feel free to modify it, use it and submit feedback. I know it’s not as generic as it could be – feel free to use it as a template for your own code.

The Evil Query

Here is the use case: I want to search all of my bookmarks by multiple tags with an “and” operation. That means I want to see bookmarks that are tagged with Java AND JavaEE, not just Java and not just JavaEE. I want to see bookmarks tagged with C# AND 3.5 not just C# and not just 3.5. For most of the project I’m using Hibernate and the Criteria API with a lot of success. In this case I was not able to massage Hibernate into creating the correct query and that’s fine. Hibernate has its uses and it is acceptable to write custom SQL when the need arises. I wrote a method to build up the query and execute it. Here is part of the method that builds up the query:

StringBuilder sqlString =
new StringBuilder(
"select distinct(b.id)
from bookmarks b ");
int counter = 0;
for(Tag tag : tags) {
String alias = "j" + Integer.toString(counter);
String joinFragment = " inner join bookmarks_to_tags {alias}
on b.id = {alias}.bookmark_id
and {alias}.tag_id = ? ";
joinFragment = joinFragment.replace("{alias}", alias);
sqlString.append(joinFragment);
counter++;
}

Right away it looks like this query will get very expensive for large tag sets. For smaller sets the query performs in a reasonable amount of time but more than 12 tags makes the query take quite a while. This is exacerbated by the fact that this query will be executed multiple times if there is a sufficiently large result set (default is over 25) to give the user a paginated view. This totally flies in the face of being nice to the database. Hammering it with a complex query over and over won’t do the users any favors.

What are my options here? I can work on making this query less complex (something that I will certainly do), but there is still the problem that, no matter how nice this query becomes, the query will be executed over and over as a user pages through the results. The complexity of the query and its repeated execution makes the result set a good candidate for caching. Caching the result set in memory with a key tied to the user or their session will allow the expensive query to be executed once with the bookmarks stored in a cache. When the user pages through the results the cache will be consulted first where the result set will be stored. I’m in the process of selecting a caching package for this project so I will post my selection and integration process soon.

Until then, I’d be happy to hear any suggestions on making this query a little nicer while preserving its “andness”.

Spring Web MVC and the benefits of pagination

Lately I’ve been working on a project that keeps my web bookmarks sync’d across multiple computers and different web browsers. The project is implemented in Java using JBoss 4.2.2, MySQL 5.0 and Spring 2.0. It’s not a very big project right now, only about 40 class files and some small number of configuration files, but the amount of data the application deals with can get fairly large.
(more…)

Spring Web MVC lists

I recently came across a blog post describing how *not* to use the Spring Web MVC form tag library. Spring Web MVC is one of those things that has a high learning curve but it pays off big once you get the hang of it. The form tag library is no exception. The original blog poster seems to want to display a list of things the form user can manipulate and then send the changes back to the app server. There are a few different ways we can do this with Spring Web MVC and some JSTL libraries so let’s take a look at the way I prefer to do it. (more…)

Response to Web Application: To Couple or Not to Couple?

I just read this thread on The Server Side and it makes me nervous that people still ask this kind of question. The thread starts off by asking “An usually annoying question when designing an action-based web application is: “Should I place everything in my action or should I separate the web logic from the business logic?” and the alarm bells in my head are going off already. (more…)