Creating Custom Login Modules In JBoss AS 7 (and Earlier)
Join the DZone community and get the full member experience.
Join For FreeJBoss 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 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:
- You declare in your web.xml:
- 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
- The roles used in your application (typically you’d something like “user” and perhaps “admin”)
- What roles are required for accessing particular URL patterns (default: none)
- Whether HTTPS is required for some parts of the application
- Login configuration – primarily whether to use browser prompt (basic) or a custom login form and a name for the login realm
- 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 (part of a security domain) 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:
- 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.
- Subclass UsernamePasswordLoginModule
- Implement javax.security.auth.spi.LoginModule 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
getUsersPassword()
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
getRoleSets()
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),
- To get values of properties declared via the <module-option ..> element in the security-domain configuration – see JBoss 5 custom module example
- To do other initialization, such as looking up a data source via JNDI – see the DatabaseServerLoginModule
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
- 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
- Deploy your login module class as a part of your webapp (no module.xml required)
- In a JAR inside WEB-INF/lib/
- 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"> <authentication> <login-module code="custom.MySimpleUsernamePasswordLoginModule" flag="required"> <!--module-option name="exampleProperty" value="exampleValue"/--> </login-module> </authentication> </security-domain>
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"?> <jboss-web> <security-domain>form-auth</security-domain> <disable-audit>true</disable-audit> </jboss-web>
The code
Download the webapp jboss-custom-login containing the custom login module MySimpleUsernamePasswordLoginModule, follow the deployment instructions in the README.
Published at DZone with permission of Jakub Holý, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments