Sunday, December 28, 2008

JSF: Frameworks and Security

One thing that has been conspicuously missing from every J2EE project I've worked on - ever - is a clean way of handling authorization. That is no less true for applications that use JSF. I shouldn't go out on a limb and say that authentication is all that clean either...actual implementations tend to range from being inextricably tied to a specific J2EE server in code to being effectively tied to a security implementation (albeit not a complete app server) because of the amount of configuration (and provision of custom security classes) that one has to do.

What a developer would like is to choose an authentication method (let's assume here that it'll be FORM-based over HTTPS, because this is quite common), and have the container do the authentication in a standard way (j_security_check and all that), with login and error pages configured in the web.xml. The same goes for logout. In addition, the login/logout code should not be inextricably tied to a server or security provider.

Furthermore, once authenticated, authorization should be easy: coming up on the end of 2008 a developer might expect a standardized way of rendering (or not rendering) JSF page elements based on authenticated role, and a standardized way of marking managed bean methods with required roles.

Note the word "standardized" - there are ways of doing all of this, even if it comes to hand-rolling it, but it ends up being a horrible mishmash. Let's bear in mind, too, that the customer usually dictates the J2EE server, and you, the developer, are possibly going to choose a framework or frameworks based on what you know they can do for you. I say possibly because that decision may not be yours either...for example, if you're working with Oracle SOA you may use Oracle across the board: JDeveloper to work with Oracle ADF etc. So, for example, you probably don't have any choice as to whether it's JBoss or OAS or Glassfish or Websphere, and at best you'll have some leeway in deciding, say, on Facelets and ICEFaces, or perhaps SEAM (maybe with ICEFaces too). But all of these decisions will be made before you start considering security.

You may think that you'll do requirements and design first, then look at how you want your security to work, and then pick servers and frameworks...I wish you luck with that.

Login Forms

Anyway, let's start with that login form. Try this little experiment...once you've set up security realms, groups and users in your application server, create a barebones web application with FORM based login. You may as well add your JSF framework(s) - it proves nothing to leave that stuff out. First off, create a target page which is JSF (say, a facelet using ICEFaces components). Perhaps that could be your welcome page in the web.xml.

Next, create two pages for your login and login error pages. This may not hold true for every server or framework, but you ought to be able to use JSF pages (say .jspx or .xhtml)...the main thing is to use only HTML tags for the login form. I know that for ICEFaces/Facelets on Glassfish V2 I can use .xhtml pages processed by the ICEFaces servlet no problem.

For the login HTML or JSP, use all HTML tags, as in the below snippet:

<form action="j_security_check" method="post">
<table>
<tr>
<td>Username:</td>
<td><input type="text" name="j_username"/></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" name="j_password"/></td>
</tr>
</table>
<input type="submit" value="Login"/>
</form>

Set up your error page similarly, except perhaps with an informative message. Make sure your web.xml has a section that looks something like:

<security-constraint>

<display-name>All Pages</display-name>
<web-resource-collection>
<web-resource-name>All</web-resource-name>
<description/>
<url-pattern>*.iface</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<description/>
<role-name>all</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>test</realm-name>
<form-login-config>
<form-login-page>/login.jspx</form-login-page>
<form-error-page>/login_error.jspx</form-error-page>
</form-login-config>
</login-config>
<security-role>
<description/>
<role-name>all</role-name>
</security-role>

You may also have to configure an application server-specific web.xml (like sun-web.xml) to map groups to roles.

If you build, deploy and run, and navigate to your JSF welcome page, you should first see the login page come up. Try putting in the username and password you set up in your security realm incorrectly first, to make sure the error page comes up. Then, if you enter the credentials correctly, you'll be redirected to your welcome page, replete with JSF effects.

What Exactly Is The Problem?

Between you and me that's actually a really good question. Because, as you may have found out already, if you modify our example to make the login page use JSF components for the form, input elements, command button etc, so-called JSF name-mangling (or ID mangling really) comes into play. For example, if your form has ID 'mainForm', then the ID for the username input (h:inputText) becomes 'mainForm:j_username'. And this is not what the container expects to see.

What people are essentially complaining about is that they can't use JSF tags for the login form itself (unless they use MyFaces Tomahawk with the 'forceId' attribute, or use a regular backing bean that resubmits to the container behind the scenes, or suchlike). Apparently the main problem with this is that they can't validate in the login form. Now, maybe it's just me, but it seems to me that if the username and/or password are wrong (including empty fields), the only answer you need is that the login failed...being able to pop up a nice message in red that a password is required is really sweet, but I imagine most users can work that one out for themselves. If necessary the login error page can provide a few tips.

In any case, a very common model in web applications is to dispense with FORM-based login, in the standard sense, entirely, and have a JSF form (possibly complex) that does have the username/password fields, and a JSF login action on a backing bean. The login method eventually ends up calling the container authentication programmatically, plus a whack of other things like redirecting based on role.

It strikes me as a bit silly to want a rich JSF login screen based on the 'j_security_check' model, but that's what a lot of people want.

Authorization

One of the things that a developer typically wants to do is to display or not display a UI component based on the security role of the authenticated user. For example, one user may only be able to view certain information on a page, but another might be able to edit certain items. Being able to control 'rendered' and 'disabled' attributes based on role is pretty handy.

This is actually not so difficult to do. It's merely that there are no security-specific attributes on standard JSF tags, although some JSF extensions do have them (MyFaces, ICEFaces, ACEGI). The JSF Security Project is looking (looked?) at making this kind of information available through EL.

Until such a thing is completely standardized it's just as easy to roll your own, unless you're already using MyFaces or IDEFaces or ACEGI. I certainly wouldn't pull in any of those only for this purpose...for example, adopting ACEGI means adopting Spring. Note also that there can be many quirks...if authenticating with JAAS you may have issues using the ICEFaces authorization attributes, and if mixing frameworks...well, sometimes they don't mix well, and sometimes they do (Tomahawk and ICEFaces don't cooperate on a single page).

Summary

In the J2EE world right now, we still spend too much time on implementing security. The requirements of 95%+ of developers in this area are simple and they are constant, and yet we tend to re-invent the wheel. As a development shop you can certainly reinvent once, and then re-use many times, but it would be nice if you never had to re-invent. What's still missing is simple off-the-shelf J2EE/JSF security solutions.