Creating Custom Login Modules In JBoss AS 7 (and Earlier)

JBoss AS 7 is neat but the documentation is still quite lacking (and error messages not as useful as they could be). This post summarizes how you can create your own JavaEE-compliant login module for authenticating users of your webapp deployed on JBoss AS. A working elementary username-password module provided.

Why to use Java EE standard authentication?

Java EE security primer

A part of the Java EE specification is security for web and EE applications, which makes it possible both to specify declarative constraints in your web.xml (such as "role X is required to access resources at URLs "/protected/*") and to control it programatically, i.e. verifying that the user has a particular role (see HttpServletRequest.isUserInRole).

It works as follows:
  1. You declare in your web.xml:
    1. Login configuration - primarily whether to use browser prompt (basic) or a custom login form and a name for the login realm
      • The custom form uses "magic" values for the post action and the fields, starting with j_, which are intercepted and processed by the server
    2. The roles used in your application (typically you'd something like "user" and perhaps "admin")
    3. What roles are required for accessing particular URL patterns (default: none)
    4. Whether HTTPS is required for some parts of the application
  2. You tell your application server how to authenticate users for that login realm, usually by associating its name with one of the available login modules in the configuration (the modules ranging from simple file-based user list to LDAP and Kerberos support). Only rarely do you need to create your own login module, the topic of this post.
If this is new for you than I strongly recommend reading The Java EE 5 Tutorial - Examples: Securing Web Applications (Form-Based Authentication with a JSP Page incl. security constraint specification, Basic Authentication with JAX-WS, Securing an Enterprise Bean, Using the isCallerInRole and getCallerPrincipal Methods).

Why to bother?

  • Declarative security is nicely decoupled from the business code
  • It's easy to propagate security information between a webapp and for example EJBs (where you can protect a complete bean or a particular method declaratively via xml or via annotations such as @RolesAllowed)
  • It's easy to switch to a different authentication mechanism such as LDAP and it's more likely that SSO will be supported

Custom login module implementation options

If one of the login modules (a.k.a. security domains) provided out of the box with JBoss, such as UsersRoles, Ldap, Database, Certificate, isn't sufficient for you then you can adjust one of them or implement your own. You can:
  1. Extend one of the concrete modules, overriding one or some of its methods to ajdust to your needs - see f.ex. how to override the DatabaseServerLoginModule to specify your own encryption of the stored passwords. This should be your primary choice, of possible.
  2. Subclass UsernamePasswordLoginModule
  3. Implement if you need maximal flexibility and portability (this is a part of Java EE, namely JAAS, and is quite complex)
JBoss EAP 5 Security Guide Ch. 12.2. Custom Modules has an excellent description of the basic modules (AbstractServerLoginModule, UsernamePasswordLoginModule) and how to proceed when subclassing them or any other standard module, including description of the key methods to implement/override. You must read it. (The guide is still perfectly applicable to JBoss AS 7 in this regard.) The custom JndiUserAndPass module example, extending UsernamePasswordLoginModule, is also worth reading - it uses module options and JNDI lookup.

Example: Custom UsernamePasswordLoginModule subclass

See the source code of MySimpleUsernamePasswordLoginModule that extends JBoss' UsernamePasswordLoginModule.

The abstract UsernamePasswordLoginModule (source code) works by comparing the password provided by the user for equality with the password returned from the method getUsersPassword, implemented by a subclass. You can use the method getUsername to obtain the user name of the user attempting login.

Implement abstract methods

Implement getUsersPassword() to lookup the user's password wherever you have it. If you do not store passwords in plain text then read how to customize the behavior via other methods below
Implement getRoleSets() (from AbstractServerLoginModule) to return at least one group named "Roles" and containing 0+ roles assigned to the user, see the implementation in the source code for this post. Usually you'd lookup the roles for the user somewhere (instead of returning hardcoded "user_role" role).

Optionally extend initialize(..) to get access to module options etc.

Usually you will also want to extend initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) (called for each authentication attempt),

Optionally override other methods to customize the behavior

If you do not store passwords in plain text (a wise choice!) and your hashing method isn't supported out of the box then you can override createPasswordHash(String username, String password,  String digestOption) to hash/encrypt the user-supplied password before comparison with the stored password.

Alternatively you could override validatePassword(String inputPassword, String expectedPassword) to do whatever conversion on the password before comparison or even do a different type of comparison than equality.

Custom login module deployment options

In JBoss AS you can
  1. Deploy your login module class in a JAR as a standalone module, independently of the webapp, under <JBoss AS 7>/modules/, together with a module.xml - described at JBossAS7SecurityCustomLoginModules
  2. Deploy your login module class as a part of your webapp (no module.xml required)
    1.  In a JAR inside WEB-INF/lib/
    2. Directly under WEB-INF/classes
In each case you have to declare a corresponding security-domain it inside JBoss configuration (standalone/configuration/standalone.xml or domain/configuration/domain.xml):

<security-domain name="form-auth" cache-type="default">
    <login-module code="custom.MySimpleUsernamePasswordLoginModule" flag="required">
      <!--module-option name="exampleProperty" value="exampleValue"/-->

The code attribute should contain the fully qualified name of your login module class and the security-domain's name must match the declaration in jboss-web.xml:

<?xml version="1.0" encoding="UTF-8"?>

The code

Download the webapp jboss-custom-login containing the custom login module MySimpleUsernamePasswordLoginModule, follow the deployment instructions in the README.

Tags: security webdev java

Copyright © 2024 Jakub Holý
Powered by Cryogen
Theme by KingMob