JBoss AS 7: Custom Login Modules
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:
- 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 (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:
- 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.
Reference: Creating Custom Login Modules In JBoss AS 7 (and Earlier) from our JCG partner Jakub Holy at the The Holy Java blog.
Hello, sorry if it’s the wrong place to post this. if it’s a bother please refer me to where. I have already tried two forums but I’m short on time so I’m hoping ou can help me ! The below foler (pic) is named jawda.war . my boss asked me to have it deployed under jboss 7… it’s not a war file or anything ! when I zip it I get the following message 13:25:47,014 ERROR [org.jboss.as.server.deployment.scanner] (DeploymentScanner-threads – 2) JBAS015051: Deployment content /opt/jboss-as/standalone/deployments/jawda.war appears to be incomplete and is not progressing toward completion. This content cannot be auto-deployed. I’m… Read more »