[jboss-cvs] jboss-seam/doc/reference/en/modules ...

Shane Bryzak Shane_Bryzak at symantec.com
Mon Jan 8 22:55:12 EST 2007


  User: sbryzak2
  Date: 07/01/08 22:55:12

  Modified:    doc/reference/en/modules  security.xml
  Log:
  finished section on customisable authentication
  
  Revision  Changes    Path
  1.4       +188 -8    jboss-seam/doc/reference/en/modules/security.xml
  
  (In the diff below, changes in quantity of whitespace are not shown.)
  
  Index: security.xml
  ===================================================================
  RCS file: /cvsroot/jboss/jboss-seam/doc/reference/en/modules/security.xml,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -b -r1.3 -r1.4
  --- security.xml	9 Jan 2007 03:16:26 -0000	1.3
  +++ security.xml	9 Jan 2007 03:55:12 -0000	1.4
  @@ -577,14 +577,14 @@
     {
       try
       {            
  -       Member member = (Member) entityManager.createQuery(
  -          "from Member where username = :username and password = :password")
  +       User user = (User) entityManager.createQuery(
  +          "from User where username = :username and password = :password")
             .setParameter("username", username)
             .setParameter("password", password)
             .getSingleResult();
     
  -       for (MemberRole mr : member.getRoles())
  -          roles.add(mr.getName());
  +       for (UserRole r : user.getRoles())
  +          roles.add(r.getName());
          
          return true;
       }
  @@ -620,7 +620,7 @@
         try
         {
            CallbackHandler cbh = SeamSecurityManager.instance().createCallbackHandler(
  -               member.getUsername(), member.getPassword());
  +               user.getUsername(), user.getPassword());
            
            LoginContext lc = SeamSecurityManager.instance().createLoginContext(cbh);
            lc.login();
  @@ -636,10 +636,16 @@
         <para>
           The first thing that happens in the above code is the creation of a <literal>CallbackHandler</literal>
           which is going to be responsible for providing the user's credentials (their username and password) to
  -        the login module.  The next step is to create a <literal>LoginContext</literal>.  There is a factory
  +        the login module.  The <literal>CallbackHandler</literal> is created by a convenience method in
  +        <literal>SeamSecurityManager</literal>, and knows how to handle <literal>NameCallback</literal> and
  +        <literal>PasswordCallback</literal> callback types.
  +      </para>
  +        
  +      <para>
  +        The next thing that happens is the creation of a <literal>LoginContext</literal>.  There is a factory
           method in <literal>SeamSecurityManager</literal> for creating this, as the login context doesn't
  -        use the standard configuration (it uses an application-specific configuration).  The last step is the
  -        call to <literal>lc.login()</literal> which calls each of the configured login modules in turn, 
  +        use the standard configuration (it uses an application-specific configuration).  The final step is the
  +        call to <literal>lc.login()</literal>, which calls each of the configured login modules in turn, 
           passing in the callback handler to each one and performing an authentication based on the configured
           login module flags.        
         </para>
  @@ -659,6 +665,180 @@
           few additional steps.  This section will walk through an example where authentication requires a
           Company ID in addition to a username and password.
         </para>
  +      
  +      <para>
  +        The first step is to subclass the <literal>SeamLoginModule</literal> class and override the 
  +        <literal>getLoginParams()</literal> method to return the appropriate parameters for the custom
  +        authentication method.
  +      </para>      
  +      
  +      <programlisting>
  +        <![CDATA[
  +public class CustomLoginModule extends SeamLoginModule 
  +{           
  +   @Override public Object[] getLoginParams()
  +      throws Exception
  +   {
  +      CompanyCallback cbCompany = new CompanyCallback("Enter company ID");
  +      NameCallback cbName = new NameCallback("Enter username");
  +      PasswordCallback cbPassword = new PasswordCallback("Enter password", false);
  +
  +      // Get the username and password from the callback handler
  +      callbackHandler.handle(new Callback[] { cbCompany, cbName, cbPassword });
  +      username = cbName.getName();
  +      
  +      return new Object[] { cbCompany.getCompanyId(), username, 
  +                            new String(cbPassword.getPassword()), roles };
  +   }        
  +}
  +        ]]>
  +      </programlisting>
  +      
  +      <para>
  +        Since <literal>CompanyCallback</literal> is a custom callback it needs to be created also:
  +      </para>
  +      
  +      <programlisting>
  +        <![CDATA[
  +public class CompanyCallback implements Callback, Serializable {
  +  private String prompt;
  +  private int companyId;
  +  public CompanyCallback(String prompt) {
  +    if (prompt == null || "".equals(prompt))
  +      throw new IllegalArgumentException();
  +    this.prompt = prompt;
  +  }        
  +  public String getPrompt() {
  +    return prompt;
  +  }
  +  public int getCompanyId() {
  +    return companyId;
  +  }
  +  public void setCompanyId(int companyId) {
  +    this.companyId = companyId;
  +  }
  +}  
  +        ]]>
  +      </programlisting>
  +      
  +      <para>
  +        So far so good, now it's time to configure the custom login module in <literal>security-config.xml</literal>:
  +      </para>
  +      
  +      <programlisting>
  +        <![CDATA[
  +  <loginmodules>
  +    <loginmodule class="com.acme.security.CustomLoginModule" flag="required">
  +      <option name="paramTypes">java.lang.Integer,java.lang.String,java.lang.String,java.util.Set</option>
  +      <option name="authMethod">#{authenticator.authenticate}</option>      
  +    </loginmodule>
  +  </loginmodules>         
  +        ]]>
  +      </programlisting>
  +      
  +      <para>
  +        The important thing to note above is that the <literal>paramTypes</literal> option is specified, based
  +        on the customised parameters that are going to be sent to the authentication method.  Now that they
  +        have been configured, it is time to write the authentication method:
  +      </para>
  +      
  +      <programlisting>
  +        <![CDATA[
  + at Name("authenticator")       
  +public class CustomAuthenticator {
  +  @In(create=true)
  +  private EntityManager entityManager;
  +
  +  public boolean authenticate(int companyId, String username, String password, Set<String> roles) 
  +  {
  +    try
  +    {            
  +       User user = (User) entityManager.createQuery(
  +          "from User where companyId = :companyId and username = :username and password = :password")
  +          .setParameter("companyId", companyId)
  +          .setParameter("username", username)
  +          .setParameter("password", password)          
  +          .getSingleResult();
  +  
  +       for (UserRole r : user.getRoles())
  +          roles.add(r.getName());
  +       
  +       return true;
  +    }
  +    catch (NoResultException ex)
  +    {
  +       log.warn("Invalid username/password");
  +       return false;
  +    }      
  +  }  
  +}
  +        ]]>
  +      </programlisting>
  +      
  +      <para>
  +        As can be seen in the above example, the customised authentication method contains the additional 
  +        <literal>companyId</literal> parameter.  The last thing to do is write a login method:
  +      </para>
  +      
  +      <programlisting>
  +        <![CDATA[
  + at Name("loginAction")
  +public class LoginAction implements LoginLocal {
  +   @In(create = true) EntityManager entityManager;
  +   @In(required = false) User user;
  +
  +   public void login()
  +   {
  +      try
  +      {
  +         CallbackHandler cbh = createCallbackHandler(user.getCompanyId(), 
  +               user.getUsername(), user.getPassword());
  +         
  +         LoginContext lc = SeamSecurityManager.instance().createLoginContext(cbh);
  +         lc.login();
  +      }
  +      catch (LoginException ex)
  +      {
  +         FacesMessages.instance().add("Invalid login");
  +      }
  +   }  
  +        
  +   private CallbackHandler createCallbackHandler(final int companyId, 
  +         final String username, final String password)
  +   {
  +      return new CallbackHandler() {
  +         public void handle(Callback[] callbacks) 
  +            throws IOException, UnsupportedCallbackException 
  +         {
  +            for (int i = 0; i < callbacks.length; i++)
  +            {
  +               if (callbacks[i] instanceof CompanyCallback)
  +                  ((CompanyCallback) callbacks[i]).setCompanyId(companyId);
  +               else if (callbacks[i] instanceof NameCallback)
  +                  ((NameCallback) callbacks[i]).setName(username);
  +               else if (callbacks[i] instanceof PasswordCallback)
  +                  ((PasswordCallback) callbacks[i]).setPassword(password.toCharArray());
  +               else
  +                  throw new UnsupportedCallbackException(callbacks[i],
  +                        "Unsupported callback");
  +            }
  +            
  +         }
  +      };
  +   }        
  +        ]]>
  +      </programlisting>
  +      
  +      <para>
  +        The most significant thing to note in the above code is the <literal>createCallbackHandler()</literal>
  +        method.  It returns a callback handler that knows how to handle a <literal>CompanyCallback</literal>, 
  +        in addition to the standard <literal>NameCallback</literal> and <literal>PasswordCallback</literal>.
  +      </para>
  +      
  +      <para>
  +        That wraps up the creation of a customised authentication process.  Based on the above steps, it should
  +        be possible to create a customised authentication based on any combination of credentials.
  +      </para>
       </sect2>
     </sect1>
   
  
  
  



More information about the jboss-cvs-commits mailing list