com.zeevbelkin.web.filter.access
Class Yaaf

java.lang.Object
  extended by com.zeevbelkin.web.filter.access.Yaaf
All Implemented Interfaces:
javax.servlet.Filter

public class Yaaf
extends java.lang.Object
implements javax.servlet.Filter

This class is an authentication, authorization and access control filter for j2ee web applications. This filter loosely emulates j2ee servlet container role based security with some extensions, it allows to easily implement login procedures for much more complicated user interaction schemes then standard container based security form login procedure. These schemas are required to provide cookie login, openid login, explicit login, etc. The filter is completely compatible with struts. The next terms are used in the documentation:

  1. Explicit login procedure occurs when a user clicks a "login" link on the site.
  2. Login on demand procedure occurs when a user, that has not logged in, tries to access any protected resource on the site. In this case, the system resumes the operation, that required the authorization, after the user successful authentication if the user has enough permissions. Implementation of a case, when a user submits a none protected form to a protected action handler with HTTP POST method is interesting especially.
  3. Cookie (none interactive) login occurs when a user has info in the cookies, enough to authenticate him/her w/o any interaction. The filter provides special support to save login info in cookie and to use such info to restore login. See SavedLoginHandler .
  4. Openid login process requires from the authentication system to redirect the user browser to a third party web site which, after the authentication there (successful or not), redirects the user browser back to the host that required the authentication. Of cause, openid login also can be "on demand" and explicit.
The explicit login procedure can be implemented with Ajax technology, see AjaxLoginPeer. The resource may be protected by the next way:
  1. Protected URLs may be described in access restriction file, specified in the web.xml filter configuration directive.
           <filter>
             <filter-name>Yaacfi</filter-name>
             <filter-class>com.zeevbelkin.web.filter.access.Yaaf</filter-class>
             
             <init-param>
                 <param-name>access-restrictions</param-name>
                 <param-value>access-restrictions.xml</param-value>
             </init-param>
             
             <init-param>
                 <param-name>login-handler</param-name>
                 <param-value>/Login.do</param-value>
             </init-param>
         </filter>
         
     
    The access restriction file contains number of constraint directives, each from them contains the role list and number of "on" directives those describe the regular expression patterns those matches to the protected URL's. Unlike web.xml url-pattern configuration directive, on directive uses real regular expressions described in Pattern . This is an example of the access restriction file.
      <security-constraints>
         <constraint  allow-to-roles="role1,role2">
             <on url-pattern="/protected/.*"/>
         </constraint>
         <constraint allow-to-roles="admin">
             <on url-pattern="/adm/.*"/>
         </constraint>
         <!-- nobody should have direct access to JSP's those implement struts actions -->
         <constraint allow-to-roles="nobody">
             <on url-pattern="/actions/.*"/>
         </constraint>
      </security-constraints>
     
    Note: the filter permits access to a user in a role on an URL only and only if all on directives where the patterns match the URL are enclosed into constraint directives where the role is listed.

  2. User roles, required to execute struts actions may be specified in the struts configuration file.
  3. Also, a servlet can set HTTP response code in 401-403 range to make the filter to begin the login on demand interaction, if a user has not logged in yet in the current session.

The filter keeps info about the user identity, and additional info required to organize the login process, in a session bean SessionSecurityInfo (this is the class name and also the bean name) that implements Principal interface. This bean may be obtained by the name or by the getUserPrincipal request method call.

Yaafci key feature is that the filter delegates the user identity check to an external, customer written servlet, that, in case of successful authentication, sets the session remote user name by setName method call, and, optionally, sets the session user role resolver by setRoleResolver call (a global user role resolver may be used to avoid need in this call). Then, in case of login on demand, the login servlet calls completeLogin method to resume the operation, that was interrupted to authenticate the user (in case of the explicit login, completeLogin does nothing). If you need the explicit login procedure, you should inform the filter about the location that will be used, by the filter, to start the login process. To provide this information it is necessary to set login-handler parameter in the filter configuration directive.
      <filter>
         <filter-name>Yaacfi</filter-name>
         <filter-class>com.zeevbelkin.web.filter.access.Yaaf</filter-class>
         <init-param>
             <param-name>access-restrictions</param-name>
             <param-value>access-restrictions.xml</param-value>
         </init-param>
         <init-param>
             <param-name>login-handler</param-name>
             <param-value>/Login.do</param-value>
         </init-param>
     </filter>
     <filter-mapping>
         <filter-name>Yaacfi</filter-name>
         <url-pattern>/*</url-pattern>
     </filter-mapping>
 
The global resolver class can be specified in the web.ini configuration file in the <filter> directive.
      <filter>
         <filter-name>Yaacfi</filter-name>
         <filter-class>com.zeevbelkin.web.filter.access.Yaaf</filter-class>
         <init-param>
         
             <param-name>userRoleResolver</param-name>
             <param-value>com.somepackage.UserRoleResolver</param-value>
        
         </init-param>
           ....
     </filter>
  
 
The global resolver class must implement Yaaf.UserRoleResolver interface. If the resolver class has a constructor that accepts ServletContext parameter, this parameter will be passed while the global resolver instantiation. When a none authenticated user posts data to an URL that requires authorization and the login on demand process begins, the filter should preserve the POST request body to restore it after the user authentication completion. The initial version of the filter preserves this data in the memory, and the maximum allowed POST request size if 256K. It is possible to change this value by setting the maxPostSize parameter.
      <filter>
         <filter-name>Yaacfi</filter-name>
         <filter-class>com.zeevbelkin.web.filter.access.Yaaf</filter-class>
            ...
         <init-param>
              <param-name>maxPostSize</param-name>
       <!-- the size if kilobytes -->
              <param-value>256</param-value>
         </init-param>
     ...
     </filter>
 
If a servlet returns HTTP 401-403 codes, the filter begins login on demand interaction. It is possible to suppress this behavior by setting handle403as401 parameter to false.
       <filter>
          <filter-name>Yaacfi</filter-name>
          <filter-class>com.zeevbelkin.web.filter.access.Yaaf</filter-class>
             ...
          <init-param>
               <param-name>handle403as401</param-name>
               <param-value>false</param-value>
          </init-param>
      ...
      </filter>
  
 
Sometimes a user begins a login interaction in one from the browser windows then leaves the window and begins a new interaction in another window. If these login interactions are "on demand", the filter keeps only info on the last resource, access to which required the authorization. Therefore, when a user returns to an old window, to continue a previously left login interaction process, in case of successful authentication, the browser will be redirected to a resource other then the resource, access to which raised the interaction in the current window. Of cause, such behavior is unwanted. To prevent this unwanted behavior the filter assigns a special ID to each "on demand" interaction process. The ID is passed as the yaacfiRqId request bean value to the application. The application should pass this parameter back to the filter using a hidden form variable with the same name (the filter can extract, also, the ID value from the HTTP request referer). Here is an example written with struts.
         <html:form action="/Login" method="post">
             <table> 
                 <tr>
                     <td>Login:</td><td>
                         <html:text property="login"/>
                     </td>
                 </tr>
                 <tr>
                     <td>Password:</td><td>
                         <html:password property="password"/>
                     </td>
                 </tr>
                 <tr>
                     <td> <html:submit/> </td>
                 </tr>
             </table>
             <input 
               name="yaacfiRqId" type="hidden" 
               value="<bean:write name="yaacfiRqId" scope="request"/>"
             />
         </html:form>
 
Then, the application can use isSavedRequestValid method to be sure that the handled request has been issued from the window from which the last login interaction was initiated.
     public ActionForward execute(
             ActionMapping mapping,
             ActionForm form,
             HttpServletRequest request,
             HttpServletResponse response
             ) throws Exception {
         DynaActionForm dac=(DynaActionForm)form;
         Yaaf.SessionSecurityInfo ssi=(Yaaf.SessionSecurityInfo)request.getUserPrincipal();
         
         synchronized (ssi.getLock()) {
             
             if (ssi.getName()!=null)
                return mapping.findForward("alreadyLoggedIn");
 
             if (!ssi.isSavedRequestValid(request))
                return mapping.findForward("outdatedLoginSequence");
             
             if ("pupkin".equals(dac.getString("login"))&&"stam".equals(dac.getString("password"))) {
                 // Yes, this guy is really Pupkin ...
                 
                 ssi.setName("pupkin");
                 
                 // Remember the user
                 // to allow to him/her to log in
                 // w/o any explicite action
                 
                 ssi.saveLogin(request,response,365);
 
                 if (ssi.isExplicitLogin()) {
                     return mapping.findForward("afterExplicitLogin");
                 } else
                     ssi.completeLogin(request,response);
                 
                 return null;
             }
             // failure
             request.setAttribute("failure","failure");
             return mapping.findForward("loginScreen");
         }
     }
 

Author:
Zeev Belkin

Nested Class Summary
static class Yaaf.RegexAccessChecker
          This class is an implementation of the UrlAccessChecker based on using of regular expressions.
static interface Yaaf.RoleResolver
          SessionSecurityInfo allows to set to an authenticated user a particular resolver.
static class Yaaf.SessionSecurityInfo
          The filter keeps info about the user identity, and additional info required to organize the login process, in this session bean that implements also Principal interface.
static interface Yaaf.UrlAccessChecker
          is an interface to a class that checks permissions of a user, roles of which can be obtained with Yaaf.RoleResolver to access a requested resource.
static interface Yaaf.UserRoleResolver
          A class that implements a global role resolver should implements this interface.
 
Constructor Summary
Yaaf()
           
 
Method Summary
 void destroy()
          Destroy method for this filter
 void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response, javax.servlet.FilterChain chain)
          
 javax.servlet.FilterConfig getFilterConfig()
          Return the filter configuration object for this filter.
 Yaaf.UserRoleResolver getRoleResolver()
          returns the global user role resolver
 SavedLoginHandler getSavedLoginHandler()
          Getter for property savedLoginHandler.
 Yaaf.SessionSecurityInfo getSSI(javax.servlet.http.HttpSession ses)
          returns SessionSecurityInfo session bean.
static java.lang.String getStackTrace(java.lang.Throwable t)
           
 void init(javax.servlet.FilterConfig filterConfig)
          Init method for this filter
static void log(java.lang.String msg)
           
static void log(java.lang.String msg, java.lang.Throwable t)
           
 void setFilterConfig(javax.servlet.FilterConfig filterConfig)
          Set the filter configuration object for this filter.
 void setRoleResolver(Yaaf.UserRoleResolver resolver)
          sets the global role resolver
 void setSavedLoginHandler(SavedLoginHandler savedLoginHandler)
          Setter for property savedLoginHandler.
 java.lang.String toString()
          Return a String representation of this object.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Constructor Detail

Yaaf

public Yaaf()
Method Detail

getSSI

public Yaaf.SessionSecurityInfo getSSI(javax.servlet.http.HttpSession ses)
returns SessionSecurityInfo session bean. If the session has no such bean, new one will be created and returned.

Parameters:
ses - the session to obtain the SSI object
Returns:
corresponding Session Security Info object

getRoleResolver

public Yaaf.UserRoleResolver getRoleResolver()
returns the global user role resolver

Returns:
the role resolver, null if the global resolver is not set.

setRoleResolver

public void setRoleResolver(Yaaf.UserRoleResolver resolver)
sets the global role resolver

Parameters:
resolver - Resolver instance. The resolver class must implement UserRoleResolver interface

doFilter

public void doFilter(javax.servlet.ServletRequest request,
                     javax.servlet.ServletResponse response,
                     javax.servlet.FilterChain chain)
              throws java.io.IOException,
                     javax.servlet.ServletException

Specified by:
doFilter in interface javax.servlet.Filter
Parameters:
request - The servlet request we are processing
result - The servlet response we are creating
chain - The filter chain we are processing
Throws:
java.io.IOException - if an input/output error occurs
javax.servlet.ServletException - if a servlet error occurs

getFilterConfig

public javax.servlet.FilterConfig getFilterConfig()
Return the filter configuration object for this filter.


setFilterConfig

public void setFilterConfig(javax.servlet.FilterConfig filterConfig)
Set the filter configuration object for this filter.

Parameters:
filterConfig - The filter configuration object

destroy

public void destroy()
Destroy method for this filter

Specified by:
destroy in interface javax.servlet.Filter

init

public void init(javax.servlet.FilterConfig filterConfig)
Init method for this filter

Specified by:
init in interface javax.servlet.Filter

toString

public java.lang.String toString()
Return a String representation of this object.

Overrides:
toString in class java.lang.Object

getStackTrace

public static java.lang.String getStackTrace(java.lang.Throwable t)

log

public static void log(java.lang.String msg)

log

public static void log(java.lang.String msg,
                       java.lang.Throwable t)

getSavedLoginHandler

public SavedLoginHandler getSavedLoginHandler()
Getter for property savedLoginHandler.

Returns:
Value of property savedLoginHandler.

setSavedLoginHandler

public void setSavedLoginHandler(SavedLoginHandler savedLoginHandler)
Setter for property savedLoginHandler.

Parameters:
savedLoginHandler - New value of property savedLoginHandler.