Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Jetty/Tutorial/JAAS"

m
 
(16 intermediate revisions by 5 users not shown)
Line 2: Line 2:
 
| introduction =  
 
| introduction =  
  
Using JAAS with Jetty requires that you complete these tasks:  
+
{{Jetty Redirect|http://www.eclipse.org/jetty/documentation/current/jaas-support.html}}
 +
 
 +
Using JAAS with Jetty is  a matter of completing the following tasks:
 +
 
 +
[[#Configuring a JAASLoginService]]
  
 
[[#Declaring an org.eclipse.jetty.plus.jaas.JAASLoginService]]
 
[[#Declaring an org.eclipse.jetty.plus.jaas.JAASLoginService]]
 +
 
[[#Creating a JAAS login module configuration file]]
 
[[#Creating a JAAS login module configuration file]]
[[#Specifying this configuration file on the Jetty run line]]
 
  
The following sections explain how to accomplish this work.
+
[[#Specifying the JAAS login module configuration file on the Jetty run line]]
 +
 
 +
You should also refer to the JAAS [http://download.oracle.com/javase/1.4.2/docs/guide/security/jaas/tutorials Tutorials] and [http://download.oracle.com/javase/1.4.2/docs/guide/security/jaas/JAASRefGuide.html Reference Guide].
 +
 
 +
| details =
 +
The following sections explain how to configure Jetty with JAAS:
 +
 
 +
=== Configuring a JAASLoginService ===
 +
 
 +
Configure a Jetty <tt>org.eclipse.jetty.plus.jaas.JAASLoginService</tt> to match the ''realm-name'' in your <tt>web.xml</tt> file. For example, if the <tt>web.xml</tt> contains a realm called "Test JAAS Realm":
 +
 
 +
<source lang="xml">
 +
<login-config>
 +
  <auth-method>FORM</auth-method>
 +
  <realm-name>Test JAAS Realm</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>
 +
</source>
 +
 
 +
=== Declaring an org.eclipse.jetty.plus.jaas.JAASLoginService  ===
 +
 
 +
Next declare the following JAASLoginService in a Jetty configuration file:
 +
 
 +
<source lang="xml">
 +
    <Call name="addBean">
 +
      <Arg>
 +
          <New class="org.eclipse.jetty.plus.jaas.JAASLoginService">
 +
          <Set name="Name">Test JAAS Realm</Set>
 +
          <Set name="LoginModuleName">xyz</Set>
 +
          </New>
 +
      </Arg>
 +
    </Call>
 +
</source>
 +
 
 +
Alternatively, of course, you can set this up in a context XML file that configures a web app:
 +
<source lang="xml">
 +
  <Set name="securityHandler">
 +
    <New class="org.eclipse.jetty.security.ConstraintSecurityHandler">
 +
    <Set name="loginService">
 +
      <New class="org.eclipse.jetty.plus.jaas.JAASLoginService">
 +
        <Set name="name">Test JAAS Realm</Set>
 +
        <Set name="loginModuleName">xyz</Set>
 +
      </New>
 +
    </Set>
 +
    </New>
 +
  </Set>
 +
</source>
 +
 
 +
{{warning|It is imperative that the ''realm-name'' element in web.xml file is ''exactly'' the same as the value of the ''name'' property of the JAASLoginService instance.}}
 +
 
 +
=== Creating a JAAS login module configuration file ===
 +
 
 +
Set up your LoginModule in a configuration file, following the [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/login/Configuration.html syntax rules]:
 +
<source lang="text">
 +
xyz {
 +
      com.acme.SomeLoginModule required debug=true;
 +
    };
 +
</source>
 +
{{warning|It is imperative that the application name to the left of the opening brace is ''exactly'' the same as the value of the ''LoginModuleName'' property specified above.}}
 +
 
 +
Read the [http://download.oracle.com/javase/1.4.2/docs/guide/security/jaas/tutorials/LoginConfigFile.html JAAS tutorial] for more information on configuration options for LoginModules.
 +
 
 +
=== Specifying the JAAS login module configuration file on the Jetty run line ===
 +
 
 +
Invoke Jetty with the JAAS configuration file you created above:
 +
<source lang="bash">
 +
> java -Djava.security.auth.login.config=mylogin.conf -jar start.jar etc/myjetty.xml
 +
</source>
 +
 
 +
== Fine Tuning the JAASLoginService ==
 +
 
 +
To allow the greatest degree of flexibility in using JAAS with web applications, the JAASLoginService supports additional configuration options. You don't ordinarily need to set these explicitly, as Jetty has defaults which work in 99% of cases. However, should you need to, you can configure:
 +
 
 +
* A policy for role-based authorization (Default: <tt>org.eclipse.jetty.plus.jaas.StrictRoleCheckPolicy</tt>).
 +
* A [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/CallbackHandler.html CallbackHandler].
 +
* A list of classnames for the [http://java.sun.com/j2se/1.4.2/docs/api/java/security/Principal.html Principal] implementation that equate to a user role (Default: <tt>org.eclipse.jetty.plus.jaas.JAASRole</tt>).
 +
 
 +
Here's an example that sets each of these:
 +
<source lang="xml">
 +
<New class="org.eclipse.jetty.plus.jaas.JAASLoginService">
 +
  <Set name="Name">xyzrealm</Set>
 +
  <Set name="LoginModuleName">xyz</Set>
 +
  <Set name="RoleCheckPolicy">
 +
    <New class="org.eclipse.jetty.plus.jaas.StrictRoleCheckPolicy"/>  <!-- default -->
 +
  </Set>
 +
  <Set name="CallbackHandlerClass">
 +
    com.acme.MyCallbackHandler
 +
  </Set>
 +
  <Set name="roleClassNames">
 +
    <Array type="java.lang.String">
 +
      <Item>org.eclipse.jetty.plus.jaas.JAASRole</Item> <!-- default -->
 +
    </Array>
 +
  </Set>
 +
</New>
 +
</source>
 +
 
 +
=== Understanding the RoleCheckPolicy ===
 +
 
 +
The RoleCheckPolicy must be an implementation of the <tt>org.eclipse.jetty.plus.jaas.RoleCheckPolicy</tt> 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 <tt>org.eclipse.jetty.plus.jaas.StrictRoleCheckPolicy</tt>, which assesses a user as having a particular role if 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 among those configured for the user.
 +
 
 +
You can temporarily assign roles to a user programmatically by using the <tt>pushRole(String rolename)</tt> method of the <tt>org.eclipse.jetty.plus.jaas.JAASUserPrincipal</tt> class.
 +
 
 +
For the majority of webapps, the default StrictRoleCheckPolicy is adequate, but you can provide your own implementation and set it on your tt>JAASLoginService</tt> instance.
 +
 
 +
=== Using the CallbackHandler ===
 +
 
 +
A [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/CallbackHandler.html CallbackHandler] is responsible for interfacing with the user to obtain usernames and credentials to be authenticated.
 +
 
 +
Jetty ships with a CallbackHandler which interfaces the information contained in the [http://download.oracle.com/javaee/5/api/javax/servlet/http/HttpServletRequest.html request] to the [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/Callback.html Callbacks] that [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/spi/LoginModule.html LoginModules] requests. You can replace this default with your own implementation if you have specific requirements not covered by the default.
 +
 
 +
=== Configuring a Role Principal Implementation Class ===
 +
 
 +
When LoginModules authenticate a user, they usually also gather all of the roles that a user has and place them inside the JAAS [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/Subject.html Subject]. As LoginModules are free to use their own implementation of the JAAS [http://java.sun.com/j2se/1.4.2/docs/api/java/security/Principal.html 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-constraints''. The example LoginModules that ship with jetty all use the <tt>org.mortbay.jetty.plus.jaas.JAASRole</tt> class. However, if you have plugged in some other LoginModules, you must configure the classnames of their role Principal implementations.
 +
 
 +
== Examining Sample Login Modules ==
 +
 
 +
At the time of writing, Jetty provides four sample LoginModule implementations:
 +
* org.eclipse.jetty.plus.jaas.spi.JDBCLoginModule
 +
* org.eclipse.jetty.plus.jaas.spi.PropertyFileLoginModule
 +
* org.eclipse.jetty.plus.jaas.spi.DataSourceLoginModule
 +
* org.eclipse.jetty.plus.jaas.ldap.LdapLoginModule
 +
 
 +
We'll take a look at all of these, but first, a word about password handling in Jetty, as it applies to all LoginModules.
 +
 
 +
=== Understanding Passwords/Credentials ===
 +
 
 +
Passwords can be stored in clear text, obfuscated or checksummed.  Use the class <tt>org.eclipse.jetty.http.security.Password</tt> to generate all varieties of passwords, the output from which you can cut and paste into property files or enter into database tables.
 +
<source lang="bash">
 +
> java -cp lib/jetty.jar org.eclipse.jetty.http.security.Password
 +
Usage - java org.eclipse.jetty.http.security.Password [<user>] <password>
 +
> java -cp lib/jetty.jar org.eclipse.jetty.http.security.Password me you
 +
you
 +
OBF:20771x1b206z
 +
MD5:639bae9ac6b3e1a84cebb7b403297b79
 +
CRYPT:me/ks90E221EY
 +
</source>
 +
Read more on [[Jetty/Howto/Secure_Passwords|securing passwords]].
 +
 
 +
=== JDBCLoginModule ===
 +
 
 +
The JDBCLoginModule stores user passwords and roles in a database that is 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:
 +
<source lang="text">
 +
jdbc {
 +
        org.eclipse.jetty.plus.jaas.spi.JDBCLoginModule required
 +
        debug="true"
 +
        dbUrl="jdbc:hsqldb:."
 +
        dbUserName="sa"
 +
        dbPassword="secret"
 +
        dbDriver="org.hsqldb.jdbcDriver"
 +
        userTable="myusers"
 +
        userField="myuser"
 +
        credentialField="mypassword"
 +
        userRoleTable="myuserroles"
 +
        userRoleUserField="myuser"
 +
        userRoleRoleField="myrole";
 +
    };
 +
</source>
 +
 
 +
There is no particular schema required for the database tables storing the authentication and role information. The properties ''userTable'', ''userField'', c''redentialField'', ''userRoleTable'', ''userRoleUserField'', ''userRoleRoleField'' configure the names of the tables and the columns within them that are used to format the following queries:
 +
<source lang="sql">
 +
select <credentialField> from <userTable> where <userField> =?
 +
select <userRoleRoleField> from <userRoleTable> where <userRoleUserField> =?
 +
</source>
 +
 
 +
Credential and role information is lazily read from the database when a previously unauthenticated user requests authentication. 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|You can store passwords in the database in plain text or encoded formats, using the Jetty [[Jetty/Howto/Secure_Passwords|password utility]].}}
 +
 
 +
=== DataSourceLoginModule ===
 +
 
 +
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 <tt>java:comp/env/$\{dnJNDIName\}</tt>.
 +
 
 +
Here is a sample login module configuration for the DataSourceLoginModule:
 +
<source lang="text">
 +
ds {
 +
      org.eclipse.jetty.plus.jaas.spi.DataSourceLoginModule required
 +
      debug="true"
 +
      dbJNDIName="ds"
 +
      userTable="myusers"
 +
      userField="myuser"
 +
      credentialField="mypassword"
 +
      userRoleTable="myuserroles"
 +
      userRoleUserField="myuser"
 +
      userRoleRoleField="myrole";
 +
  };
 +
</source>
 +
 
 +
=== PropertyFileLoginModule ===
 +
 
 +
With this login module implementation, the authentication and role information is read from a property file.
 +
<source lang="text">
 +
props {
 +
          org.eclipse.jetty.plus.jaas.spi.PropertyFileLoginModule required
 +
          debug="true"
 +
          file="/somewhere/somefile.props";
 +
      };
 +
</source>
 +
 
 +
The file parameter is the location of a properties file of the same format as the <tt>etc/realm.properties</tt> example file. The format is:
 +
<source lang="text"><username>: <password>[,<rolename> ...]
 +
</source>
 +
Here's an example:
 +
<source lang="text">fred: OBF:1xmk1w261u9r1w1c1xmq,user,admin
 +
harry: changeme,user,developer
 +
tom: MD5:164c88b302622e17050af52c89945d44,user
 +
dick: CRYPT:adpexzg3FUZAk,admin
 +
</source>
 +
 
 +
The contents of the file are fully read in and cached in memory the first time a user requests authentication.
 +
 
 +
=== LdapLoginModule ===
 +
 
 +
The LdapLoginModule ships in a separate jar, in <tt>$JETTY-HOME/lib/ext/jetty-ldap-jaas.jar</tt>. It requires JDK1.5 or above.
 +
 
 +
<source lang="text">
 +
 
 +
ldaploginmodule {
 +
  org.eclipse.jetty.plus.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";
 +
  };
 +
</source>
 +
 
 +
==== Configuring the Role Principal Implementation Class for the LdapLoginModule<ref>Jay Jaeger, IT Management Consultant at the State of Wisconsin, provided this example. In turn, Jaeger writes, "I would never have figured this out had it not been for the article at
 +
http://mail-archives.apache.org/mod_mbox/karaf-issues/201012.mbox/%3C30176778.74721292097241071.JavaMail.jira@thor%3E ." </ref>====
 +
 
 +
The LdapLoginModule does not deal with the situation of user -> some group -> role group, as it assigns users directly to the role group. If you want to specify role occupants by groups within an organization, you need the ability to sort them in this way. The following example shows how to do so by configuring ''RoleClassNames'' information. This example implements [http://waffle.codeplex.com/ WAFFLE], which lets you run Jetty under Windows with Active Directory.
 +
 
 +
<source lang="xml">
 +
<New class="org.mortbay.jetty.plus.jaas.JAASUserRealm">
 +
  <Set name="name">MyTestRealm</Set>
 +
  <Set name="LoginModuleName">Jaas</Set>
 +
  <Set name="RoleClassNames">
 +
    <Array type="java.lang.String">
 +
      <Item>waffle.jaas.UserPrincipal</Item>
 +
      <Item>waffle.jaas.RolePrincipal</Item>
 +
    </Array>
 +
  </Set>
 +
</New>
 +
</source>
 +
 
 +
Along with:
 +
 
 +
<source lang="text">
 +
Jaas {
 +
waffle.jaas.WindowsLoginModule sufficient debug=true;
 +
};
 +
</source>
 +
 
 +
Add the necessary WAFFLE classes to the Jetty lib directory. Then start Jetty:
 +
 
 +
<source lang="bash">
 +
> java -Djava.security.auth.login.config=$jetty.home/etc/waffle-login.conf -jar start.jar etc/myjetty.xml
 +
</source>
 +
 
 +
=== Writing Your Own LoginModule ===
 +
 
 +
If you want to implement your own custom LoginModule, there are two classes you should be familiar with:
 +
 
 +
<source lang="java">
 +
AbstractLoginModule.java
 +
 
 +
package org.eclipse.jetty.plus.jaas.spi;
 +
 
 +
public abstract class AbstractLoginModule implements LoginModule
 +
{
 +
  ...
 +
  public abstract UserInfo getUserInfo (String username) throws Exception;
 +
}
 +
</source>
 +
 
 +
<source lang="java">
 +
UserInfo.java
 +
 
 +
package org.eclipse.jetty.plus.jaas.spi;
 +
 
 +
public class UserInfo
 +
{
 +
 
 +
  public UserInfo (String userName, Credential credential, List roleNames)
 +
  {
 +
    ...
 +
  }
 +
 
 +
  public String getUserName()
 +
  {
 +
    ...
 +
  }
 +
 
 +
  public List getRoleNames ()
 +
  {
 +
    ...
 +
  }
 +
 
 +
  public boolean checkCredential (Object suppliedCredential)
 +
  {
 +
    ...
 +
  }
 +
}
 +
</source>
 +
 
 +
The <tt>org.eclipse.jetty.plus.jaas.spi.AbstractLoginModule</tt> implements all of the <tt>javax.security.auth.spi.LoginModule</tt> methods. All you need to do is to implement the <tt>getUserInfo</tt> method to return an <tt>org.eclipse.jetty.plus.jaas.UserInfo</tt> instance which encapsulates the username, password and role names as <tt>java.lang.Strings</tt> for a user.
 +
 
 +
The AbstractLoginModule does not support any caching, so if you want to cache UserInfo (as, for example, does the <tt>org.mortbay.jetty.plus.jaas.spi.PropertyFileLoginModule</tt>, then you must provide this yourself.
 +
 
 +
== Examining an Example JAAS WebApp ==
 +
 
 +
There is an example of authentication and web authorization in the Jetty distribution in <tt>examples/test-jaas-webapp</tt>. It uses the PropertyFileLoginModule to perform authentication based on a simple properties file.  To use it with the [http://jetty.mortbay.org/maven-plugin/index.html jetty maven plugin]:
 +
<source lang="text">
 +
> cd examples/test-jaas-webapp
 +
> mvn jetty:run
 +
</source>
 +
 
 +
Alternatively, to use it instead with Jetty standalone:
 +
<source lang="text">
 +
> cd examples/test-jaas-webapp
 +
> mvn clean install
 +
> cd ../../
 +
> java -jar start.jar etc/jetty.xml etc/jetty-jaas.xml
 +
</source>
 +
 
 +
Then surf to <nowiki>http://localhost:8080/test-jaas/index.html</nowiki>.
 +
 
 +
== Other Goodies ==
  
=== Declaring an org.eclipse.jetty.plus.jaas.JAASLogin Service ===
+
=== RequestParameterCallback ===
  
Configure a Jetty org.eclipse.jetty.plus.jaas.JAASLoginService to match the &lt;realm-name&gt; in your web.xml file. For example, if the web.xml contains a realm called "xyzrealm":
+
As all servlet containers intercept and process a form submission with action <tt>j_security_check</tt>, it is usually not possible to insert any extra input fields onto a login form with which to perform authentication: you may only pass <tt>j_username</tt> and <tt>j_password</tt>. 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 <tt>org.mortbay.jetty.plus.jaas.callback.RequestParameterCallback</tt>. This callback handler gives you access to all parameters passed in the form submission. To use it, in the <tt>login()</tt> method of your custom login module, add the RequestParameterCallback to the list of callback handlers the login module uses, tell it which parameters you are want, and then get the value of the parameter back. Here's an example:
  
 +
<source lang="java">
 +
FooLoginModule.java
  
 +
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
 +
        .
 +
        .
 +
        .
 +
    }
 +
</source>
  
<source lang="xml"></source> &amp;lt;login-config&amp;gt;&amp;lt;auth-method&amp;gt;FORM&amp;lt;/auth-method&amp;gt; &amp;lt;realm-name&amp;gt;xyzrealm&amp;lt;/realm-name&amp;gt; &amp;lt;form-login-config&amp;gt;&amp;lt;form-login-page&amp;gt;/login/login&amp;lt;/form-login-page&amp;gt; &amp;lt;form-error-page&amp;gt;/login/error&amp;lt;/form-error-page&amp;gt; &amp;lt;/form-login-config&amp;gt; &amp;lt;/login-config&amp;gt; fckLRfckLRThen the following JAASLoginService would be declared in a Jetty configuration file:fckLR<source lang="xml"></source> &amp;lt;call name="addBean"&amp;gt;&amp;lt;arg&amp;gt;&amp;lt;new class="org.eclipse.jetty.plus.jaas.JAASLoginService"&amp;gt;&amp;lt;set name="Name"&amp;gt;Test JAAS Realm&amp;lt;/set&amp;gt; &amp;lt;set name="LoginModuleName"&amp;gt;xyz&amp;lt;/set&amp;gt; &amp;lt;/new&amp;gt; &amp;lt;/arg&amp;gt; &amp;lt;/call&amp;gt; fckLRfckLRAlternatively of course, you can set this up in a context xml file that configures a web app:fckLR<source lang="xml"></source> &amp;lt;set name="securityHandler"&amp;gt;&amp;lt;new class="org.eclipse.jetty.security.ConstraintSecurityHandler"&amp;gt;&amp;lt;set name="loginService"&amp;gt;&amp;lt;new class="org.eclipse.jetty.plus.jaas.JAASLoginService"&amp;gt;&amp;lt;set name="name"&amp;gt;Test JAAS Realm&amp;lt;/set&amp;gt; &amp;lt;set name="loginModuleName"&amp;gt;xyz&amp;lt;/set&amp;gt; &amp;lt;/new&amp;gt; &amp;lt;/set&amp;gt; &amp;lt;/new&amp;gt; &amp;lt;/set&amp;gt; fckLRfckLR{{warning|It is imperative that the contents of the realm-name element in web.xml file and the value of the 'name' property of the JAASLoginService instance are ''exactly'' the same}}fckLRfckLR=== Step 2 ===fckLRfckLRSet up your LoginModule in a configuration file, following the [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/login/Configuration.html syntax rules]:fckLR<source lang="text"></source> xyz { com.acme.SomeLoginModule required debug=true; }; fckLR{{warning|It is imperative that the application name to the left of the opening brace is ''exactly'' the same as the value of the 'LoginModuleName' property specified in Step 2.}}fckLRfckLRfckLR=== Step 3 ===fckLRfckLRInvoke jetty with the jaas configuration file you created in step 2:fckLR<source lang="bash"></source> &amp;amp;gt; java -Djava.security.auth.login.config=mylogin.conf -jar start.jar etc/myjetty.xml fckLRfckLR== A Closer Look at the JAASLoginService ==fckLRfckLRTo 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:fckLRfckLR* a policy for role-based authorization (Default: org.eclipse.jetty.plus.jaas.StrictRoleCheckPolicy)fckLR* a [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/CallbackHandler.html CallbackHandler] (Default: org.eclipse.jetty.plus.jaas.callback.DefaultCallbackHandler)fckLR* a list of classnames for the [http://java.sun.com/j2se/1.4.2/docs/api/java/security/Principal.html Principal] implementation that equate to a user role (Default: org.eclipse.jetty.plus.jaas.JAASRole)fckLRfckLRHere's an example of setting each of these (to their default values):fckLR<source lang="xml"></source> &amp;lt;new class="org.eclipse.jetty.plus.jaas.JAASLoginService"&amp;gt;&amp;lt;set name="Name"&amp;gt;xyzrealm&amp;lt;/set&amp;gt; &amp;lt;set name="LoginModuleName"&amp;gt;xyz&amp;lt;/set&amp;gt; &amp;lt;set name="RoleCheckPolicy"&amp;gt;&amp;lt;new class="org.eclipse.jetty.plus.jaas.StrictRoleCheckPolicy"&amp;gt;&amp;lt;/new&amp;gt; &amp;lt;/set&amp;gt; &amp;lt;set name="CallbackHandlerClass"&amp;gt;org.eclipse.jetty.plus.jaas.callback.DefaultCallbackHandler &amp;lt;/set&amp;gt; &amp;lt;set name="roleClassNames"&amp;gt;&amp;lt;array type="java.lang.String"&amp;gt;&amp;lt;item&amp;gt;org.eclipse.jetty.plus.jaas.JAASRole&amp;lt;/item&amp;gt; &amp;lt;/array&amp;gt; &amp;lt;/set&amp;gt; &amp;lt;/new&amp;gt; fckLRfckLR=== RoleCheckPolicy ===fckLRfckLRThe RoleCheckPolicy must be an implementation of the org.eclipse.jetty.plus.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.plus.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.fckLRfckLRRoles can be temporarily assigned to a user programmatically by using the pushRole(String rolename) method of the org.eclipse.jetty.plus.jaas.JAASUserPrincipal class.fckLRfckLRFor 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.fckLRfckLR=== CallbackHandler ===fckLRfckLRA [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/CallbackHandler.html CallbackHandler] is responsible for interfacing with the user to obtain usernames and credentials to be authenticated.fckLRfckLRJetty ships with the org.eclipse.jetty.plus.jaas.DefaultCallbackHandler which interfaces the information contained in the [http://jetty.mortbay.org/apidocs/javax/servlet/http/HttpServletRequest.html request] to the [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/callback/Callback.html Callbacks] that are requested by [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/spi/LoginModule.html LoginModules]. You can replace this default with your own implementation if you have specific requirements not covered by the default.fckLRfckLR=== Role Principal Implementation Class ===fckLRfckLRWhen LoginModules authenticate a user, they usually also gather all of the roles that a user has and place them inside the JAAS [http://java.sun.com/j2se/1.4.2/docs/api/javax/security/auth/Subject.html Subject]. As LoginModules are free to use their own implementation of the JAAS [http://java.sun.com/j2se/1.4.2/docs/api/java/security/Principal.html 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 &amp;amp;amp;lt;security-constraint&amp;amp;amp;gt;s. The example LoginModules that ship with jetty all use the org.mortbay.jetty.plus.jaas.JAASRole class. However, if you have plugged in some other LoginModules, you must configure the classnames of their role Principal implementations.fckLRfckLR=== Sample Login Modules ===fckLRfckLRAt the time of writing, Jetty provides four sample LoginModule implementations:fckLR* org.eclipse.jetty.plus.jaas.spi.JDBCLoginModulefckLR* org.eclipse.jetty.plus.jaas.spi.PropertyFileLoginModulefckLR* org.eclipse.jetty.plus.jaas.spi.DataSourceLoginModulefckLR* org.eclipse.jetty.plus.jaas.ldap.LdapLoginModulefckLRfckLRWe'll take a look at all of these, but first, a word about password handling in Jetty, as it applies to all LoginModules.fckLRfckLR=== Passwords/Credentials ===fckLRfckLRPasswords can be stored in clear text, obfuscated or checksummed. The class org.eclipse.jetty.http.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.fckLR<source lang="bash"></source> &amp;amp;gt; java -cp lib/jetty.jar org.eclipse.jetty.http.security.Password Usage - java org.eclipse.jetty.http.security.Password [&amp;lt;user&amp;gt;] &amp;lt;password&amp;gt;&amp;amp;gt; java -cp lib/jetty.jar org.eclipse.jetty.http.security.Password me you you OBF:20771x1b206z MD5:639bae9ac6b3e1a84cebb7b403297b79 CRYPT:me/ks90E221EY fckLRRead more on [[Jetty/Howto/Secure_Passwords|securing passwords]].fckLRfckLR=== JDBCLoginModule ===fckLRfckLRThe 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.fckLRfckLRHere is an example login module configuration file entry for it using an HSQLDB driver:fckLR jdbc { org.eclipse.jetty.plus.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"; }; fckLRfckLRThere 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:fckLR select &amp;lt;credentialfield&amp;gt;from &amp;lt;usertable&amp;gt;where &amp;lt;userfield&amp;gt;=? select &amp;lt;userrolerolefield&amp;gt;from &amp;lt;userroletable&amp;gt;where &amp;lt;userroleuserfield&amp;gt;=? fckLRfckLRCredential 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.fckLRfckLRNote that passwords can be stored in the database in plain text or encoded formats, using the Jetty [[Jetty/Howto/Secure_Passwords|password utility]].fckLRfckLR=== DataSourceLoginModule ===fckLRfckLRSimilar 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\}fckLRfckLRHere is a sample login module configuration for it:fckLR ds { org.eclipse.jetty.plus.jaas.spi.DataSourceLoginModule required debug="true" dbJNDIName="ds" userTable="myusers" userField="myuser" credentialField="mypassword" userRoleTable="myuserroles" userRoleUserField="myuser" userRoleRoleField="myrole"; }; fckLRfckLR=== PropertyFileLoginModule ===fckLRfckLRWith this login module implementation, the authentication and role information is read from a property file.fckLR props { org.eclipse.jetty.plus.jaas.spi.PropertyFileLoginModule required debug="true" file="/somewhere/somefile.props"; }; fckLRfckLRThe file parameter is the location of a properties file of the same format as the etc/realm.properties example file. The format is:fckLR&amp;lt;username&amp;gt;: &amp;lt;password&amp;gt;[,&amp;lt;rolename&amp;gt;...] fckLRHere's an example:fckLRfred: OBF:1xmk1w261u9r1w1c1xmq,user,admin harry: changeme,user,developer tom: MD5:164c88b302622e17050af52c89945d44,user dick: CRYPT:adpexzg3FUZAk,admin fckLRfckLRThe contents of the file are fully read in and cached in memory the first time a user requests authentication.fckLRfckLR=== LdapLoginModule ===fckLRfckLRNote that the LdapLoginModule ships in a separate jar, in $JETTY-HOME/lib/ext/jetty-ldap-jaas.jar. It requires JDK1.5 or above.fckLRfckLR ldaploginmodule { org.eclipse.jetty.plus.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"; }; fckLRfckLR=== Writing Your Own ===fckLRfckLRIf you want to implement your own custom LoginModule, there are two classes to be familiar with:fckLRfckLR AbstractLoginModule.java package org.eclipse.jetty.plus.jaas.spi; public abstract class AbstractLoginModule implements LoginModule { ... public abstract UserInfo getUserInfo (String username) throws Exception; } fckLRfckLR UserInfo.java package org.eclipse.jetty.plus.jaas.spi; public class UserInfo { public UserInfo (String userName, Credential credential, List roleNames) { ... } public String getUserName() { ... } public List getRoleNames () { ... } public boolean checkCredential (Object suppliedCredential) { ... } } fckLRfckLRThe org.eclipse.jetty.plus.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.plus.jaas.UserInfo instance which encapsulates the username, password and role names (note: as java.lang.Strings) for a user.fckLRfckLRThe AbstractLoginModule does not support any caching, so if you want to cache UserInfo (e.g. as does the org.mortbay.jetty.plus.jaas.spi.PropertyFileLoginModule) then you must provide this yourself.fckLRfckLR=== Example JAAS WebApp ===fckLRfckLRThere is an example of authentication and web authorization in the jetty distribution in examples/test-jaas-webapp. It uses the PropertyFileLoginModule to perform authentication based on a simple properties file. To use it with the [http://jetty.mortbay.org/maven-plugin/index.html jetty maven plugin]:fckLR &amp;amp;gt; cd examples/test-jaas-webapp &amp;amp;gt; mvn jetty:run fckLRfckLRAlternatively, to use it instead with jetty standalone:fckLR &amp;amp;gt; cd examples/test-jaas-webapp &amp;amp;gt; mvn clean install &amp;amp;gt; cd ../../ &amp;amp;gt; java -jar start.jar etc/jetty.xml etc/jetty-jaas.xml fckLRfckLRThen surf to <nowiki>http://localhost:8080/test-jaas/index.html</nowiki>fckLRfckLR== Other Goodies ==fckLRfckLR=== RequestParameterCallback ===fckLRfckLRAs 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.mortbay.jetty.plus.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:fckLRfckLR FooLoginModule.java 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 . . . } fckLR| category = [[Category:Jetty Tutorial]]fckLR}}
 
  
  
  
<br>
+
== References ==
  
&lt;/rolename&gt;&lt;/password&gt;&lt;/username&gt;&lt;/userroleuserfield&gt;&lt;/userroletable&gt;&lt;/userrolerolefield&gt;&lt;/userfield&gt;&lt;/usertable&gt;&lt;/credentialfield&gt;&lt;/password&gt;&lt;/user&gt;
+
<references />
 +
| category = [[Category:Jetty Tutorial]]
 +
}}

Latest revision as of 13:45, 30 April 2013



Introduction

Warning2.png
Jetty 7 and Jetty 8 are now EOL (End of Life)




THIS IS NOT THE DOCUMENTATION YOU ARE LOOKING FOR!!!!!






All development and stable releases are being performed with Jetty 9 and Jetty 10.






This wiki is now officially out of date and all content has been moved to the Jetty Documentation Hub






Direct Link to updated documentation: http://www.eclipse.org/jetty/documentation/current/jaas-support.html


Using JAAS with Jetty is a matter of completing the following tasks:

#Configuring a JAASLoginService

#Declaring an org.eclipse.jetty.plus.jaas.JAASLoginService

#Creating a JAAS login module configuration file

#Specifying the JAAS login module configuration file on the Jetty run line

You should also refer to the JAAS Tutorials and Reference Guide.

Details

The following sections explain how to configure Jetty with JAAS:

Configuring a JAASLoginService

Configure a Jetty org.eclipse.jetty.plus.jaas.JAASLoginService to match the realm-name in your web.xml file. For example, if the web.xml contains a realm called "Test JAAS Realm":

<login-config>
  <auth-method>FORM</auth-method>
  <realm-name>Test JAAS Realm</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>

Declaring an org.eclipse.jetty.plus.jaas.JAASLoginService

Next declare the following JAASLoginService in a Jetty configuration file:

    <Call name="addBean">
      <Arg>
          <New class="org.eclipse.jetty.plus.jaas.JAASLoginService">
           <Set name="Name">Test JAAS Realm</Set>
           <Set name="LoginModuleName">xyz</Set>
          </New>
      </Arg>
    </Call>

Alternatively, of course, you can set this up in a context XML file that configures a web app:

  <Set name="securityHandler">
    <New class="org.eclipse.jetty.security.ConstraintSecurityHandler">
     <Set name="loginService">
       <New class="org.eclipse.jetty.plus.jaas.JAASLoginService">
         <Set name="name">Test JAAS Realm</Set>
         <Set name="loginModuleName">xyz</Set>
       </New>
     </Set>
    </New>
  </Set>
Warning2.png
It is imperative that the realm-name element in web.xml file is exactly the same as the value of the name property of the JAASLoginService instance.


Creating a JAAS login module configuration file

Set up your LoginModule in a configuration file, following the syntax rules:

xyz {
       com.acme.SomeLoginModule required debug=true;
    };
Warning2.png
It is imperative that the application name to the left of the opening brace is exactly the same as the value of the LoginModuleName property specified above.


Read the JAAS tutorial for more information on configuration options for LoginModules.

Specifying the JAAS login module configuration file on the Jetty run line

Invoke Jetty with the JAAS configuration file you created above:

> java -Djava.security.auth.login.config=mylogin.conf -jar start.jar etc/myjetty.xml

Fine Tuning the JAASLoginService

To allow the greatest degree of flexibility in using JAAS with web applications, the JAASLoginService supports additional configuration options. You don't ordinarily need to set these explicitly, as Jetty has defaults which work in 99% of cases. However, should you need to, you can configure:

  • A policy for role-based authorization (Default: org.eclipse.jetty.plus.jaas.StrictRoleCheckPolicy).
  • A CallbackHandler.
  • A list of classnames for the Principal implementation that equate to a user role (Default: org.eclipse.jetty.plus.jaas.JAASRole).

Here's an example that sets each of these:

<New class="org.eclipse.jetty.plus.jaas.JAASLoginService">
  <Set name="Name">xyzrealm</Set>
  <Set name="LoginModuleName">xyz</Set>
  <Set name="RoleCheckPolicy">
    <New class="org.eclipse.jetty.plus.jaas.StrictRoleCheckPolicy"/>  <!-- default -->
  </Set>
  <Set name="CallbackHandlerClass">
    com.acme.MyCallbackHandler
  </Set>
  <Set name="roleClassNames">
    <Array type="java.lang.String">
      <Item>org.eclipse.jetty.plus.jaas.JAASRole</Item> <!-- default -->
    </Array>
  </Set>
</New>

Understanding the RoleCheckPolicy

The RoleCheckPolicy must be an implementation of the org.eclipse.jetty.plus.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.plus.jaas.StrictRoleCheckPolicy, which assesses a user as having a particular role if 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 among those configured for the user.

You can temporarily assign roles to a user programmatically by using the pushRole(String rolename) method of the org.eclipse.jetty.plus.jaas.JAASUserPrincipal class.

For the majority of webapps, the default StrictRoleCheckPolicy is adequate, but you can provide your own implementation and set it on your tt>JAASLoginService</tt> instance.

Using the CallbackHandler

A CallbackHandler is responsible for interfacing with the user to obtain usernames and credentials to be authenticated.

Jetty ships with a CallbackHandler which interfaces the information contained in the request to the Callbacks that LoginModules requests. You can replace this default with your own implementation if you have specific requirements not covered by the default.

Configuring a Role Principal Implementation Class

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-constraints. The example LoginModules that ship with jetty all use the org.mortbay.jetty.plus.jaas.JAASRole class. However, if you have plugged in some other LoginModules, you must configure the classnames of their role Principal implementations.

Examining Sample Login Modules

At the time of writing, Jetty provides four sample LoginModule implementations:

  • org.eclipse.jetty.plus.jaas.spi.JDBCLoginModule
  • org.eclipse.jetty.plus.jaas.spi.PropertyFileLoginModule
  • org.eclipse.jetty.plus.jaas.spi.DataSourceLoginModule
  • org.eclipse.jetty.plus.jaas.ldap.LdapLoginModule

We'll take a look at all of these, but first, a word about password handling in Jetty, as it applies to all LoginModules.

Understanding Passwords/Credentials

Passwords can be stored in clear text, obfuscated or checksummed. Use the class org.eclipse.jetty.http.security.Password to generate all varieties of passwords, the output from which you can cut and paste into property files or enter into database tables.

> java -cp lib/jetty.jar org.eclipse.jetty.http.security.Password
Usage - java org.eclipse.jetty.http.security.Password [<user>] <password>
> java -cp lib/jetty.jar org.eclipse.jetty.http.security.Password me you
you
OBF:20771x1b206z
MD5:639bae9ac6b3e1a84cebb7b403297b79
CRYPT:me/ks90E221EY

Read more on securing passwords.

JDBCLoginModule

The JDBCLoginModule stores user passwords and roles in a database that is 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.plus.jaas.spi.JDBCLoginModule required
         debug="true"
         dbUrl="jdbc:hsqldb:."
         dbUserName="sa"
         dbPassword="secret"
         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. 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.png
You can store passwords in the database in plain text or encoded formats, using the Jetty password utility.


DataSourceLoginModule

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 the DataSourceLoginModule:

ds {
       org.eclipse.jetty.plus.jaas.spi.DataSourceLoginModule required
       debug="true"
       dbJNDIName="ds"
       userTable="myusers"
       userField="myuser"
       credentialField="mypassword"
       userRoleTable="myuserroles"
       userRoleUserField="myuser"
       userRoleRoleField="myrole";
   };

PropertyFileLoginModule

With this login module implementation, the authentication and role information is read from a property file.

props {
          org.eclipse.jetty.plus.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.

LdapLoginModule

The LdapLoginModule ships in a separate jar, in $JETTY-HOME/lib/ext/jetty-ldap-jaas.jar. It requires JDK1.5 or above.

ldaploginmodule {
   org.eclipse.jetty.plus.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";
   };

Configuring the Role Principal Implementation Class for the LdapLoginModule[1]

The LdapLoginModule does not deal with the situation of user -> some group -> role group, as it assigns users directly to the role group. If you want to specify role occupants by groups within an organization, you need the ability to sort them in this way. The following example shows how to do so by configuring RoleClassNames information. This example implements WAFFLE, which lets you run Jetty under Windows with Active Directory.

<New class="org.mortbay.jetty.plus.jaas.JAASUserRealm">
  <Set name="name">MyTestRealm</Set>
  <Set name="LoginModuleName">Jaas</Set>
  <Set name="RoleClassNames">
    <Array type="java.lang.String">
      <Item>waffle.jaas.UserPrincipal</Item>
      <Item>waffle.jaas.RolePrincipal</Item>
    </Array>
  </Set>
</New>

Along with:

Jaas {
waffle.jaas.WindowsLoginModule sufficient debug=true;
};

Add the necessary WAFFLE classes to the Jetty lib directory. Then start Jetty:

> java -Djava.security.auth.login.config=$jetty.home/etc/waffle-login.conf -jar start.jar etc/myjetty.xml

Writing Your Own LoginModule

If you want to implement your own custom LoginModule, there are two classes you should be familiar with:

AbstractLoginModule.java
 
package org.eclipse.jetty.plus.jaas.spi;
 
public abstract class AbstractLoginModule implements LoginModule
{
  ...
  public abstract UserInfo getUserInfo (String username) throws Exception;
}
UserInfo.java
 
package org.eclipse.jetty.plus.jaas.spi;
 
public class UserInfo
{
 
  public UserInfo (String userName, Credential credential, List roleNames)
  {
    ...
  }
 
  public String getUserName()
  {
    ...
  }
 
  public List getRoleNames ()
  {
    ...
  }
 
  public boolean checkCredential (Object suppliedCredential)
  {
     ...
  }
}

The org.eclipse.jetty.plus.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 an org.eclipse.jetty.plus.jaas.UserInfo instance which encapsulates the username, password and role names as java.lang.Strings for a user.

The AbstractLoginModule does not support any caching, so if you want to cache UserInfo (as, for example, does the org.mortbay.jetty.plus.jaas.spi.PropertyFileLoginModule, then you must provide this yourself.

Examining an Example JAAS WebApp

There is an example of authentication and web authorization in the Jetty distribution in examples/test-jaas-webapp. It uses the PropertyFileLoginModule to perform authentication based on a simple properties file. To use it with the jetty maven plugin:

> cd examples/test-jaas-webapp
> mvn jetty:run

Alternatively, to use it instead with Jetty standalone:

> cd examples/test-jaas-webapp
> mvn clean install
> cd ../../
> java -jar start.jar etc/jetty.xml etc/jetty-jaas.xml

Then surf to http://localhost:8080/test-jaas/index.html.

Other Goodies

RequestParameterCallback

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.mortbay.jetty.plus.jaas.callback.RequestParameterCallback. This callback handler gives you access to all parameters 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 parameters you are want, and then get the value of the parameter back. Here's an example:

FooLoginModule.java
 
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
        .
        .
        .
     }



References

  1. Jay Jaeger, IT Management Consultant at the State of Wisconsin, provided this example. In turn, Jaeger writes, "I would never have figured this out had it not been for the article at http://mail-archives.apache.org/mod_mbox/karaf-issues/201012.mbox/%3C30176778.74721292097241071.JavaMail.jira@thor%3E ."

Back to the top