I started developing on the Java EE platform 15 years ago (yep, you read that right). At that time, the only way to authenticate in a webapp was through a callback. Recent Java EE versions offer alternatives.
This post aims at describing both, how they work and their respective benefits.<!--more-→
The legacy way
The way available since J2EE 1.3 (and perhaps earlier, but then I’m not an archeologist) is through a callback mechanism: the platform only asks for a user’s credentials when he tries to access a protected resource. If credentials are accepted, then the user is authenticated. Then his access rights are matched to the access rights required to acces the resource. If they match, he’s allowed to; otherwise, he receives a 403 error.
This raises a few questions:
- what is a resource?
- how to attach access rights to a resource?
- how to ask a user for credentials?
- how to configure access rights of a user?
- how to logout?
Resources and roles
In Java EE webapps, resources are plain URLs:
admin
secure/configure
- etc.
Resources can be grouped, using a simple pattern (it allows *
at the beginning or the end, but not more).
To protect a resource or a group of them, one associates it with a role. A role is just a string.
Both roles and their associated resources are configured in the web.xml
deployment descriptor:
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<security-constraint>
<web-resource-collection>
<web-resource-name>Administration</web-resource-name>
<url-pattern>/administration/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<role-name>admin</role-name>
</security-role>
</web-app>
Roles must be declared in a separate <security-role> tag, before being used in <security-constraint> .
|
Asking for credentials
Java EE offers 4 ways to authenticate:
- Basic
- Digest
- Form
- and Client certificate
We will cover only Basic and Form, as they are the most used.
- Basic
-
Basic authentication is based on the browser. When an unauthenticated user attempts to access a protected resource, the platform returns a 401 HTTP status code. The browser intercepts the response, and displays a native popup asking for login and password credentials. If credentials check fail, then the user is shown the popup again, until they succeed.
Whether the authenticated user can access the resource or not depends on his role(s). <?xml version="1.0" encoding="UTF-8"?> <web-app> <login-config> <auth-method>BASIC</auth-method> </login-config> </web-app>
- Form
-
Form authentication is based on the server. When an unauthenticated user attempts to access a protected resource, the platform returns the configured login form.
<?xml version="1.0" encoding="UTF-8"?> <web-app> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/WEB-INF/page/login.jsp</form-login-page> <form-error-page>/WEB-INF/page/error.jsp</form-error-page> </form-login-config> </login-config> </web-app>
Logging out
In the old days, the way to log out an authenticated user very was straightforward:
response.invalidate();
Unfortunately, the above line effectively destroys everything that was stored in the session, including data unrelated to authentication, such as user preferences (e.g. theme).
Using annotations
The version 3 of the Servlet API takes advantage of annotations and allows to use them to restrict roles allowed to access a servlet.
For example, the following is the equivalent of the first XML snippet:
@WebServlet("/adminstration/foo")
@ServletSecurity(
@HttpConstraint(rolesAllowed = "admin")
)
public class FooServlet extends HttpServlet {
}
Obviously, it works very well for one specific servlet. However, if different servlets need to be protected by the same role, then the annotation has to be replicated on each of them.
The programmatic API
The main issue with the authentication callback described above is that it was designed for page-based navigation in mind. If the webapp is a Single-Page Application, URL handling tricks are necessary to make it work.
Permission checking
Java EE allows to guard URLs, or even URL/HTTP method pairs.
For SPAs, this is not enough.
Even in traditional webapps, the different doXXX()
methods might not be granular enough for some use-cases.
Also, sections of JSPs might be different depending on the user role.
Hence, the Servlet API offers the HttpServletRequest.isUserInRole(String role)
method to check whether a user has a specific role or not.
JSTL offers no out-of-the-box equivalent for that. |
Authenticating
Like for checking, authentication callbacks bound to URLs are not easily integrated with SPAs.
To handle that, the Servlet API v3 offers the HttpServletRequest.login(String username, String password)
method to programmatically authenticate the user.
The symmetric HttpServletRequest.logout()
method allows to un-authenticate an authenticated user without losing data stored in this user session.
Conclusion
As always, it pays to know tools available. And for that, it’s necessary to keep up-to-date with the latest changes in the API.