![]() Version: 9.3.10.M0 |
private support for your internal/customer projects ... custom extensions and distributions ... versioned snapshots for indefinite support ... scalability guidance for your apps and Ajax/Comet projects ... development services for sponsored feature development
JAAS implements a Java version of the standard Pluggable Authentication Module (PAM) framework.
JAAS can be used for two purposes:
JAAS authentication is performed in a pluggable fashion. This permits applications to remain independent from underlying authentication technologies. New or updated authentication technologies can be plugged under an application without requiring modifications to the application itself. Applications enable the authentication process by instantiating a LoginContext object, which in turn references a Configuration to determine the authentication technology(ies), or LoginModule(s), to be used in performing the authentication. Typical LoginModules may prompt for and verify a username and password. Others may read and verify a voice or fingerprint sample.
See Java Authentication and Authorization Service (JAAS) Reference Guide for more information about JAAS.
Many application servers support JAAS as a means of bringing greater flexibility to the declarative security models of the J2EE (now known as the JavaEE) specification. Jetty support for JAAS provides greater alternatives for servlet security, and increases the portability of web applications.
The JAAS support aims to dictate as little as possible whilst providing a sufficiently flexible infrastructure to allow users to drop in their own custom LoginModules.
Using JAAS with jetty is very simply a matter of declaring a
org.eclipse.jetty.jaas.JAASLoginService
, creating a jaas login module
configuration file and specifying it on the jetty run line. Let’s look
at an example.
Configure a jetty org.eclipse.jetty.jaas.JAASLoginService
to match the
<realm-name> in your web.xml file. For example, if the web.xml
contains a realm called "xyz" like so:
<login-config>
<auth-method>FORM</auth-method>
<realm-name>xyz</realm-name>
<form-login-config>
<form-login-page>/login/login</form-login-page>
<form-error-page>/login/error</form-error-page>
</form-login-config>
</login-config>
Then you need to create a JAASLoginService with the matching name of "xyz":
<New class="org.eclipse.jetty.jaas.JAASLoginService">
<Set name="Name">Test JAAS Realm</Set>
<Set name="LoginModuleName">xyz</Set>
</New>
Caution
The name of the realm-name that you declare in
web.xml
must match exactly the name of your JAASLoginService.
You can declare your JAASLoginService in a couple of different ways:
If you have more than one webapp that you would like to use the same security infrastructure, then you can declare your JAASLoginService in a top-level jetty xml file as a bean that is added to the org.eclipse.jetty.server.Server. Here’s an example:
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call name="addBean">
<Arg>
<New class="org.eclipse.jetty.jaas.JAASLoginService">
<Set name="name">Test JAAS Realm</Set>
<Set name="LoginModuleName">xyz</Set>
</New>
</Arg>
</Call>
</Configure>
Alternatively, you can use a JAASLoginService with just a specific webapp by creating a context xml file for the webapp, and specifying the JAASLoginService in it:
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="securityHandler">
<New class="org.eclipse.jetty.security.ConstraintSecurityHandler">
<Set name="loginService">
<New class="org.eclipse.jetty.jaas.JAASLoginService">
<Set name="name">Test JAAS Realm</Set>
<Set name="loginModuleName">xyz</Set>
</New>
</Set>
</New>
</Set>
</Configure>
Set up your LoginModule
in a configuration file, following the
syntax
rules :
xyz {
com.acme.SomeLoginModule required debug=true;
};
Caution
It is imperative that the application name on the first line is exactly the same as the
LoginModuleName
of your JAASLoginService.
You may find it convenient to name this configuration file as
etc/login.conf
because, as we will see below, some of the wiring up
for jaas has been done for you.
You now need to invoke jetty with support for jaas. There are 2 aspects to this:
java.security.auth.login.config
To accomplish the above, use the jetty startup modules mechanism to add the jaas module:
java -jar start.jar --add-to-startd=jaas
Note
The top level of the distribution does not have the jaas module enabled by default. However, there are several demo webapps - including a jaas webapp - available in the
demo-base
directory of the distribution which has pre-enabled the jaas module.
Now you will have a file named start.d/jaas.ini
, which contains:
--module=jaas
jaas.login.conf=etc/login.conf
The jaas.login.conf
property refers to the location of your
LoginModule configuration file that you established in
Step 2. If you called it etc/login.conf
, then your
work is done. Otherwise, change the value of the` jaas.login.conf`
property to be the location of your LoginModule configuration file.
Jetty will automatically use this property to set the value of the
System property java.security.auth.login.config.
To allow the greatest degree of flexibility in using JAAS with web
applications, the JAASLoginService
supports a couple of configuration
options. Note that you don’t ordinarily need to set these explicitly, as
jetty has defaults which will work in 99% of cases. However, should you
need to, you can configure:
org.eclipse.jetty.jaas.StrictRoleCheckPolicy
)org.eclipse.jetty.jaas.callback.DefaultCallbackHandler
)org.eclipse.jetty.jaas.JAASRole
)Here’s an example of setting each of these (to their default values):
<New class="org.eclipse.jetty.jaas.JAASLoginService">
<Set name="Name">Test JAAS Realm</Set>
<Set name="LoginModuleName">xyz</Set>
<Set name="RoleCheckPolicy">
<New class="org.eclipse.jetty.jaas.StrictRoleCheckPolicy"/>
</Set>
<Set name="CallbackHandlerClass">
org.eclipse.jetty.jaas.callback.DefaultCallbackHandler
</Set>
<Set name="roleClassNames">
<Array type="java.lang.String">
<Item>org.eclipse.jetty.jaas.JAASRole</Item>
</Array>
</Set>
</New>
The RoleCheckPolicy must be an implementation of the
org.eclipse.jetty.jaas.RoleCheckPolicy
interface and its purpose is to
help answer the question "is User X in Role Y" for role-based
authorization requests. The default implementation distributed with
jetty is the org.eclipse.jetty.jaas.StrictRoleCheckPolicy
, which will
assess a user as having a particular role iff that role is at the top of
the stack of roles that have been temporarily pushed onto the user or if
the user has no temporarily assigned roles, the role is amongst those
configured for the user.
Roles can be temporarily assigned to a user programmatically by using
the pushRole(String rolename) method of the
org.eclipse.jetty.jaas.JAASUserPrincipal
class.
For the majority of webapps, the default StrictRoleCheckPolicy will be quite adequate, however you may provide your own implementation and set it on your JAASLoginService instance.
A CallbackHandler is responsible for interfacing with the user to obtain usernames and credentials to be authenticated.
Jetty ships with the org.eclipse.jetty.jaas.DefaultCallbackHandler
which interfaces the information contained in the request to the
Callbacks that are requested by LoginModules. You can replace this
default with your own implementation if you have specific requirements
not covered by the default.
When LoginModules authenticate a user, they usually also gather all of
the roles that a user has and place them inside the JAAS Subject. As
LoginModules are free to use their own implementation of the JAAS
Principal to put into the Subject, jetty needs to know which Principals
represent the user and which represent his/her roles when performing
authorization checks on <security-constraint>s. The example LoginModules
that ship with jetty all use the org.eclipse.jetty.jaas.JAASRole
class. However, if you have plugged in some other LoginModules, you must
configure the classnames of their role Principal implementations.
Note
Passwords can be stored in clear text, obfuscated or checksummed. The class
org.eclipse.jetty.util.security.Password
should be used to generate all varieties of passwords,the output from which can be cut and pasted into property files or entered into database tables.+ See more on this under the Configuration section on securing passwords.
The JDBCLoginModule stores user passwords and roles in a database that are accessed via JDBC calls. You can configure the JDBC connection information, as well as the names of the table and columns storing the username and credential, and the name of the table and columns storing the roles.
Here is an example login module configuration file entry for it using an HSQLDB driver:
jdbc {
org.eclipse.jetty.jaas.spi.JDBCLoginModule required
debug="true"
dbUrl="jdbc:hsqldb:."
dbUserName="sa"
dbDriver="org.hsqldb.jdbcDriver"
userTable="myusers"
userField="myuser"
credentialField="mypassword"
userRoleTable="myuserroles"
userRoleUserField="myuser"
userRoleRoleField="myrole";
};
There is no particular schema required for the database tables storing the authentication and role information. The properties userTable, userField, credentialField, userRoleTable, userRoleUserField, userRoleRoleField configure the names of the tables and the columns within them that are used to format the following queries:
select <credentialField> from <userTable>
where <userField> =?
select <userRoleRoleField> from
<userRoleTable> where <userRoleUserField>
=?
Credential and role information is lazily read from the database when a previously unauthenticated user requests authentication. Note that this information is only cached for the length of the authenticated session. When the user logs out or the session expires, the information is flushed from memory.
Note that passwords can be stored in the database in plain text or encoded formats - see "Passwords/Credentials" note above.
Similar to the JDBCLoginModule, but this LoginModule uses a DataSource
to connect to the database instead of a jdbc driver. The DataSource is
obtained by doing a jndi lookup on java:comp/env/${dnJNDIName}
Here is a sample login module configuration for it:
ds {
org.eclipse.jetty.jaas.spi.DataSourceLoginModule required
debug="true"
dbJNDIName="ds"
userTable="myusers"
userField="myuser"
credentialField="mypassword"
userRoleTable="myuserroles"
userRoleUserField="myuser"
userRoleRoleField="myrole";
};
With this login module implementation, the authentication and role information is read from a property file.
props {
org.eclipse.jetty.jaas.spi.PropertyFileLoginModule required
debug="true"
file="/somewhere/somefile.props";
};
The file parameter is the location of a properties file of the same format as the etc/realm.properties example file. The format is:
<username>: <password>[,<rolename> ...]
Here’s an example:
fred: OBF:1xmk1w261u9r1w1c1xmq,user,admin
harry: changeme,user,developer
tom: MD5:164c88b302622e17050af52c89945d44,user
dick: CRYPT:adpexzg3FUZAk,admin
The contents of the file are fully read in and cached in memory the first time a user requests authentication.
Here’s an example:
ldaploginmodule {
org.eclipse.jetty.jaas.spi.LdapLoginModule required
debug="true"
contextFactory="com.sun.jndi.ldap.LdapCtxFactory"
hostname="ldap.example.com"
port="389"
bindDn="cn=Directory Manager"
bindPassword="directory"
authenticationMethod="simple"
forceBindingLogin="false"
userBaseDn="ou=people,dc=alcatel"
userRdnAttribute="uid"
userIdAttribute="uid"
userPasswordAttribute="userPassword"
userObjectClass="inetOrgPerson"
roleBaseDn="ou=groups,dc=example,dc=com"
roleNameAttribute="cn"
roleMemberAttribute="uniqueMember"
roleObjectClass="groupOfUniqueNames";
};
If you want to implement your own custom LoginModule, there are two
classes to be familiar with
org.eclipse.jetty.jaas.spi.AbstractLoginModule
and
org.eclipse.jetty.jaas.spi.UserInfo
.
The org.eclipse.jetty.jaas.spi.AbstractLoginModule
implements all of
the javax.security.auth.spi.LoginModule
methods. All you need to do is
to implement the getUserInfo method to return a
org.eclipse.jetty.jaas.UserInfo
instance which encapsulates the
username, password and role names (note: as java.lang.Strings) for a
user.
The AbstractLoginModule does not support any caching, so if you want to
cache UserInfo (eg as does the
org.eclipse.jetty.jaas.spi.PropertyFileLoginModule
) then you must
provide this yourself.
As all servlet containers intercept and process a form submission with
action j_security_check, it is usually not possible to insert any extra
input fields onto a login form with which to perform authentication: you
may only pass j_username
and j_password
. For those rare occasions
when this is not good enough, and you require more information from the
user in order to authenticate them, you can use the JAAS callback
handler org.eclipse.jetty.jaas.callback.RequestParameterCallback
. This
callback handler gives you access to all parameters that were passed in
the form submission. To use it, in the login() method of your custom
login module, add the RequestParameterCallback to the list of callback
handlers the login module uses, tell it which params you are interested
in, and then get the value of the parameter back. Here’s an example:
public class FooLoginModule extends AbstractLoginModule
{
public boolean login()
throws LoginException
{
Callback[] callbacks = new Callback[3];
callbacks[0] = new NameCallback();
callbacks[1] = new ObjectCallback();
//as an example, look for a param named "extrainfo" in the request
//use one RequestParameterCallback() instance for each param you want to access
callbacks[2] = new RequestParameterCallback ();
((RequestParameterCallback)callbacks[2]).setParameterName ("extrainfo");
callbackHandler.handle(callbacks);
String userName = ((NameCallback)callbacks[0]).getName();
Object pwd = ((ObjectCallback)callbacks[1]).getObject();
List paramValues = ((RequestParameterCallback)callbacks[2]).getParameterValues();
//use the userName, pwd and the value(s) of the parameter named "extrainfo" to
//authenticate the user
}
An example webapp using jaas can be found in our git repo: