Showing posts with label servlet. Show all posts
Showing posts with label servlet. Show all posts

Friday, January 22, 2010

Implementing User Authentication

Earlier in the blog, I showed how to use a 3rd party OpenId service to allow people to login to your site using their Google account, their Facebook account, etc.

That isn't appropriate for every type of site, so now I'll show how to allow people to register at your site and create a login and password specific for your site.

I'd planned on using Spring Security for this, so that the authentication would be robust. But, Spring Security has some dependencies on the Spring framework. Supposedly getting it to work without Spring is possible, but not knowing either product it probably won't happen in the limited amount of time I have available (sorry for those of you searching for how to do just that).

I do, by the way, highly recommend Spring for industrial strength applications built on Java servlets. It just isn't appropriate for the class that I'm teaching. So, the authentication we'll roll here won't be industrial grade. But it'll be a start.

Let's talk for a moment about the characteristics of a good authentication mechanism.

First, it must be impossible to fool. For example, an authentication mechanism that simply avoided displaying members only links to non-members would not prevent non-members from typing those links in directly. The server must authenticate on any restricted page load.

Second, it must be easy to use. The more places we have to touch to implement authentication for a page, the higher the chance that we'll get it wrong. In particular, if we have to write Java code for each page we want protected, the chances are good that we'll get it wrong sooner or later.

We'll use Java Servlet filters to accomplish this. A filter is basically a piece of Java code that is run before a servlet is run. The filter can allow the servlet to run, or it can display a page itself and block the servlet. This happens without adding code to the servlet itself.

First step is to set up our web.xml file to use the filter (okay, so we haven't written the filter yet, but editing web.xml is the easy part so we'll get it out of the way). Add something like the following to your web.xml:


<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>omega.server.AuthenticationFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/members/*</url-pattern>
</filter-mapping>


This is like registering a servlet, in that you specify a URL pattern that triggers the filter. In my case, I used /members/*. This means that any URL under the members directory will trigger the filter. This will require changing my servlet URL mappings around a bit, so that any page that should be restricted to logged in users is mapped under /members, and any page that should be visible to anyone is not. So far for me, that's basically just the ProfileServlet, but I'll make a mental note to put future members only pages under /members.

Now we have to write the AuthenticationFilter class. This is similar to writing a servlet. Create a new class in your server package. There's no superclass for this one, but you will implement an interface. So in the new class dialog, click on the Add button to the right of the (empty) interface list, and type in Filter. You want the javax.servlet.Filter interface.

You'll get some Java code that leaves stubs for three methods: init, destroy, and doFilter. The primary purpose of the init is to save off the FilterConfig passed in, so create a private data member for doing that:


private FilterConfig filterConfig = null;

public void init(FilterConfig arg0) throws ServletException {
filterConfig = arg0;

}


You may not need that FilterConfig instance, but it can be used to get the ServletContext of the servlet that you're filtering.

The actual work of authentication will be done in the doFilter method. Here's the basic idea (not working code, but algorithm):


if (user authenticates correctly)
  chain.doFilter(request, response);
else
{
  User the response argument to generate error output, or redirect to a login form
}


So calling chain.doFilter passes the request on to the next filter in the chain, meaning that the AuthenticationFilter is happy with the request. If authentication fails, do something other than calling chain.doFilter.

The details of pulling a user's password from the datastore and comparing it to one passed into the HTTP request we've already covered. You should also be able to write the registration form and servlet to allow users to create their user account in the first place (and you can do verification of their email, too). You should also know what you need to know to have a "I forgot my password" link that will email a new password to them.

The one bit that we do still need to cover is storing passwords in an encrypted form. Storing user's passwords in plain text is bad form, so we won't do it.

Next post, encrypting passwords.

Friday, January 8, 2010

Unique Display Names

I'm going to focus on new techniques in the coming posts, because the semester is starting.

I've decided to make display names unique in the system. I'm not especially happy with this, since it leads to funky display names like Dave5386. But, if I'm going to provide some sort of forum interaction eventually, I also need a way to differentiate between people who might decide to use the same display name.

I could simply require and display some context to the display name, such as "Jay, Muskingum University". But, providing information online for kids that might be used to locate them is not a good idea.

So, I'll go with unique display names. This means I need to change what I already have a bit.

RPXResults.java is what first puts the user into the datastore. Right now it's getting the user name from the RPX records. That isn't guaranteed to be unique. I could auto-generate a unique name, but then they might not go back and edit it.

So instead, RPXResults.java will, when a user logs in for the first time, redirect to the page for editing their profile. They will be required to update their profile with a unique name before doing using the rest of the site as a logged in user.

While I'm at it, I also want to modify RPXResults.java so that instead of redirecting to /front when a user logs in and they do have their profile info set, it redirects to the page they were trying to access. So if they save a bookmark on My Contests, and try to go to that after the session has expired, it'll take them to the login page and then to My Contests after they log in. That's more user-friendly, and best to do now before I have too many of those sorts of pages to edit.

Redirect to Target Page

We'll do the redirect to the target page first. This requires me to pass into /rpxresults the target page. Here's a page at RPXNow about passing query parameters through the token URL.

Unfortunately, that didn't work for me. The extra query parameter simply never came through to RPXResults.java.

Luckily we do have another option. We can store the destination URL as a cookie on the local machine and fetch it from RPXResults.java.

We'll do FrontPage.java first as an example of how to modify the servlets. Here's the code to save the destination URL as a cookie:


Cookie cookie = new Cookie ("destinationURL", "/front");
resp.addCookie(cookie);


I added this to just before the template is displayed. Now we need to modify RPXResults.java to use the value of this cookie as the destination URL. Here's the code that would replace the resp.redirect ("/front") line:


String destination = null;
Cookie [] cookies = req.getCookies();

if (destination == null && cookies != null)
{
for(int i=0; i<cookies.length; i++)
{
Cookie cookie = cookies[i];

if ("destinationURL".equals(cookie.getName()))
destination = cookie.getValue();
}
}

if (destination == null)
destination = "/front";

resp.sendRedirect(destination);


Now that RPXResults.java is set up, I can go back and modify the ProfileServlet and AdminServlet classes to add the cookie in the same way that the FrontPage servlet does.

Now if I remember to do that for every servlet I add after this, the user will always get to the page they wanted to get to, even if they have to log in first.

That's probably long enough for one post, so I'll do unique user names in the next.

Friday, December 18, 2009

Handling Profile Updates

We have a profile page with a form that allows logged in users to update their name. Now we need to add handling to ProfileServlet that will update the data store when the Save Changes button is clicked.

The trick is to know if the servlet is being called by clicking on a My Profile link, or as a result of the Save Changes button being clicked. The secret to that is to look at the form submission data provided to every servlet.

The HTTPServletRequest object (passed into the doPost and doGet methods by the web server) contains a method called getParameter. Pass it the name of a form element, and it will give you back the values associated with that element. If the element doesn't exist, you'll get null back.

So code like this:


if (req.getParameter("submit") == null)
{
// Servlet called because of link click
}
else
{
// Servlet called because of button click
}


will allow you to decide which situation is calling the servlet, so you can make the appropriate response.

In the case of the profile page, I still want to display the form after they click submit, but I want to update the data store, too. So I'm going to have code like this just after I fetch the user record from the data store, but just before I get any values out of it:


User user = results.get(0);

if (req.getParameter("submit") != null)
{
user.setName(req.getParameter("name").trim());
pm.makePersistent(user);
}

String name = user.getName ();



This completes the form processing round trip. We really should add some error checking and error messages, too. For example, someone shouldn't be able to save a blank name. Add appropriate error handling and messages to your own projects.

Following this same pattern, you can add however many profile fields you need. Just remember to add them to the User object, too, if they aren't already in there.

Wednesday, December 16, 2009

Adding The Profile Page

I want the user to be able to edit their profile page, so they can change their name to something other than what RPX reported. And so they can add information not given in RPX (for example, in my contest site I might want their birthday so I can calculate their age, and so I can display a Happy Birthday message on their birthday).

In the FreeMarker template for the front page I put the link for the profile page as /profile. That will have to map to a Java servlet that displays the profile editing page for the currently logged in user, and saves changes to the data store.

This will also let us set up a pattern for pages that require the user to be logged in to view, as well as a pattern for pages that do form processing.

So let's add a handler for the profile page, and start out by requiring the user to be logged in to view it. If they aren't logged in, they'll get a message and a Sign In link.

If you don't remember how to add a new servlet, read my original post on Java Servlets. Add a new servlet mapped to /profile. I'll call mine ProfileServlet, so the code for it will be in ProfileServlet.java in my project.server package.

In ProfileServlet.java, I'll use code like this to show one page if they're logged in, and another if they're not:


Map root = new HashMap();
String template = "profileNotLoggedIn.ftl";

if (session != null)
{
// Get the User's name
Long userid = (Long) session.getAttribute ("userid");
PersistenceManager pm = PMF.get().getPersistenceManager();

Query query = pm.newQuery("select from omega.server.User where id == idParam");
query.declareParameters("Long idParam");

try
{
List<User> results = (List<User>) query.execute(userid);
User user = results.get(0);
String name = user.getName ();
Integer userLevel = user.getUserLevel ();
root.put("loggedIn", true);
root.put("name", name);
root.put("userLevel", userLevel);
template = "profileLoggedIn.ftl";
}
finally
{
query.closeAll();
pm.close();
}
}
else
{
root.put("loggedIn", false);
}

Template temp = config.getTemplate(template);

PrintWriter out = resp.getWriter();

try
{
temp.process(root, out);
out.flush();
} catch (TemplateException e)
{
e.printStackTrace(out);
}


Notice how the FreeMarker template we use depends on whether the user is logged in or not. This allows us to separate out those pages for web design purposes, rather than forcing the web designer to put logic into the pages.

I'll need to create both FreeMarker templates. profileNotLoggedIn.ftl will just be a simple error page with the standard links across the top. profileLoggedIn.ftl will display a form, populating the fields with the data from the data model.

I'll create those templates by copying over my Front.ftl file and adding the extra bits. Next post we'll see how to centralize some of the common navigation elements so we aren't duplicating them between templates.

Here's the form from profileLoggedIn.ftl:


<form action="/profile" method="POST">
<input type="text" name="name" value="${name}">
<input type="submit" name="submit" value="Save Changes">
</form>


This is the simplest form possible that will do the job. It really should be a bit fancier, providing at least a label for the edit field.

Right now clicking Save Changes just redisplays the form. After we remove the duplication among the templates, we'll add in handling of the form to actually update the data store.

Monday, June 15, 2009

Executing A FreeMarker Template

In our front page servlet, we need to execute the FreeMarker template we created.

We start by creating a Configuration instance. This only needs to be done once per application, so we can use a singleton pattern (remember the PMF class?) Only this time we don't have a utility class provided to us, so you'll have to write your own. Follow the PMF pattern and create a singleton for getting Configuration instances.

You'll end up with something like this (I've left off the package and import statements to save space):


public class ConfigFactory
{
  private static Configuration configInstance = null;
  private ConfigFactory() {}
  public static Configuration get(ServletContext context)
  {
   if (configInstance == null)
   {
    configInstance = new Configuration();
    configInstance.setServletContextForTemplateLoading(context, "WEB-INF/templates");
    configInstance.setObjectWrapper(new DefaultObjectWrapper());
   }

   return configInstance;
  }
}


Note that I have it looking for templates in WEB-INF/templates. That gets templates out of the way, which is generally a good idea to keep things tidy. Now would be a good time to right click on WEB-INF and create a new folder named templates, and to drag Front.ftl into that new directory.

In our front page servlet, we can now use the singleton class to get a Configuration instance.


Configuration config = ConfigFactory.get(getServletContext ());


Now we need to create the data model for the template. In the case of our simplified front page, the data model just has a boolean named loggedIn. Before we can do that we need to know if they're logged in or not.

If they are logged in, we'll have their user id in the session. So let's get the session and check.


HttpSession session = req.getSession(false);


I pass false into getSession this time because if there isn't already a session, I don't want to create one. If no session already existed, getSession(false) returns null.


boolean loggedIn = false;

if (session != null)
  loggedIn = true;


We could very well have used getAttribute to get the actual user id and looked up the User record in the datastore, but this will do for the moment. Now that we have our loggedIn variable, let's create the data model:


Map root = new HashMap();
root.put("loggedIn", loggedIn);


That's it. A data model is nothing more than a map of values. You can nest maps inside the map to create objects with fields for the template to use.

Now we need to get the template:


Template temp = config.getTemplate("Front.ftl");


If you named your template something other than Front, you'll want to modify that. Now that we have the template, let's execute it:


PrintWriter out = resp.getWriter();

try
{
  temp.process(root, out);
  out.flush();
} catch (TemplateException e)
{
  e.printStackTrace(out);
}


Note that printing the stack trace to the user's web browser isn't the best way to handle errors. We'll want to do something better than that later, but for now it'll serve.

We have one last piece before this will ever show "Sign Out". That's to cause the RPXResults servlet to redirect the user's browser back to the front page after the session has been set.

At the bottom of doPost in RPXResults, this should do it:


resp.sendRedirect("/front");


Assuming that /front is what you put in for the url mapping for your front page servlet.

You should now be able to run the application, click Sign In, go through the RPX process and end up back at the front page with the Sign Out link showing. This shows a very simple example of using templates to display pages with dynamic content. If our content is going to be updated according to user action, we'll use GWT widgets, but for simple content that stays the same once it's generated on the page, templates are a great option.

A One Page Web Site

We'll now create a one-page web site that properly reflects the user's logged in status. Where our current first page has the "Sign In" link, that will be "Sign In" if the user is not logged in, or "Sign Out" if the user is logged in.

Our RPXResults servlet will do its thing silently, and then redirect back to the first page. We'll have to change the first page to be a servlet rather than a static HTML page, so that we can dynamically change it based on the user's logged in status.

Note that we could do the same thing with a static HTML page that included GWT components. We could in a GWT pane display the "Sign In" or "Sign Out" links, and use GWT calls to the server to see if the user is logged in or not. Eventually we'll get around to that sort of thing, but for now I'd like to keep it as simple as possible.

First step, change the main page of the site to be a servlet. Right click on war/WEB-INF/web.xml and choose Open With->Text Editor.

Enter the info for a new servlet (you remember how, right?) to be used as the main page. You don't need to have a servlet mapping for this servlet, but it might come in handy later to have it map to something like /front. Then copy the welcome-file-list section down to just before /web-app, and change the welcome file to be the name you gave to the servlet.

Now create the servlet (which you also remember, right?). Go ahead and put in the code to create the PrintWriter.

Let me describe the easy way (at least in the short term) to do this, which makes the code very ugly and the HTML almost impossible to maintain. Then we'll look at the hard way to do it, which makes the code nicer and the HTML easier to maintain.

The easy way is to output the HTML using our PrintWriter:


writer.println ("<html>");
writer.println ("<head>");
writer.println ("<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">");


Etc, printing the entire HTML file using the PrintWriter. Notice the need to escape the double quotes inside the output using a backslash. This method is easy, because we don't have to work with anything but the servlet, but it makes it nearly impossible to easily change the HTML later.

The harder way (in the short run at least) is to use a templating engine. A templating engine is server side code that allows us to write the HTML in a file of its own, with a mini-programming language for making decisions. The servlet will execute that template file and provide any information it needs to make decisions. The result is the HTML that should be sent to the client.

There are lots of templating engines out there. I'm going to use FreeMarker, which integrates well with Java servlets and Eclipse.

So before we can use FreeMarker, we have to install it and the Eclipse plugin for it.

Session Handling

Normal web pages are "stateless". This means that every time they're loaded is like the first. They don't keep any information around about the user loading them.

This doesn't work for web applications, where we do want to keep information about the user around from page to page. To carry information over from page load to page load, we need to create something called a "session".

A session is basically an id that gets sent to the browser and stored in a cookie. Future page loads on the same domain will send that session id along with the page request. The server then uses that session id as the primary key in a datastore lookup to load in any information you wanted to carry over from page load to page load.

By default, session support in the Java version of App Engine is not enabled. Right click on war/WEB-INF/appengine-web.xml and choose Open With->Text Editor. Just before the ending /appengine-web-app tag, put this line:


<sessions-enabled>true</sessions-enabled>


This makes the class HttpSession available to your servlets. The HttpSession class is how you'll set data on one page and get it on another page. Inside RPXResults.java, after we have the User instance, we can get the HttpSession instance like this:


HttpSession session = req.getSession ();


The HttpSession class then has methods for getting to data you've previously set, and for setting data. Think of it like a map between a property name and a property value. Use the setAttribute method to set property values, and the getAttribute method to get property values.

Another useful method on HttpSession is setMaxInactiveInterval, where you specify the number of seconds the session will live without activity (e.g. the timeout). Pass in a negative value to make the session live forever (or at least until the user clears their cookies).

What do we need to store in the session? Not much, really. The datastore already will have our User records (and other entity records we'll add later), so in the session we just need enough to get to the User in the datastore.

So let's store the user's primary key in the session. From there we can look up the rest of their fields.


HttpSession session = req.getSession();
session.setAttribute("userid", user.getId());


And that's it. In other code we can use getAttribute to fetch the user's id, and then use the datastore to fetch the full User instance for that id.

So the user is officially logged in at this point, although we don't have much of a web site yet to demonstrate that fact. So that's next, to develop a very simple web site that will reflect the logged in status of the user.