giovedì 7 giugno 2012

Get the Realm (or LoginService) instance from a ServletConxtext in Tomcat 7 and Jetty 7.5

Container-Managed Authentication is not always the best choose for implement security in web application. A typical example is when an home page have to be personalized if the current user is logged or not. With Container Managed Security, if a page isn't protected by a security-constraint, also if the current user is logged, the request.getUserPrinipal() is null.
See http://stackoverflow.com/questions/7795012/request-getuserprincipal-got-null.

A way to skip over the problem is to implement Application-Managed Authentication in web application. This means that web-app developer have to implements authentication/authorization.

But not everything is lost! We can reuse the Realm configured within application server to authenticate/authorize application's users.
There are two snippet of code that retrieve the Realm authenticator from ServletContext.

Get Tomcat 7 Realm from ServletContext:

 ApplicationContextFacade acf = (ApplicationContextFacade) servletContext;  
 Field privateField = ApplicationContextFacade.class.getDeclaredField("context");  
 privateField.setAccessible(true);  
 ApplicationContext appContext = (ApplicationContext) privateField.get(acf);  
 Field privateField2 = ApplicationContext.class.getDeclaredField("context");  
 privateField2.setAccessible(true);  
 StandardContext stdContext = (StandardContext) privateField2.get(appContext);  
 realm = stdContext.getRealm();  

The realm variable is an instance of org.apache.catalina.Realm, and you can call on this the authenticate method to authenticate and get roles of users:

 Principal principal = realm.authenticate(user, password);  
 if (principal == null)  
    return null;  
 GenericPrincipal genericPrincipal = (GenericPrincipal) principal;  
 return new DemoPrincipal(principal, genericPrincipal.getRoles());  


This is the counter part for Jetty 7.5. Be ware that normally Jetty don't allow to reach the container classes from web application class loader, we need to disable this option, see http://docs.codehaus.org/display/JETTY/Classloading.
 WebAppContext.Context context = ((org.eclipse.jetty.webapp.WebAppContext.Context) servletContext);  
 ContextHandler contextHandler = context.getContextHandler();  
 ConstraintSecurityHandler securityHandler = contextHandler  
       .getChildHandlerByClass(org.eclipse.jetty.security.ConstraintSecurityHandler.class);  
 loginService = securityHandler.getLoginService();  

 UserIdentity login = loginService.login(user, password);  
 if (login == null)  
     return null;  
 Principal principal = login.getUserPrincipal();  
 Field privateField = DefaultUserIdentity.class.getDeclaredField("_roles");  
 privateField.setAccessible(true);  
 String[] roles = (String[]) privateField.get(login);    
 return new DemoPrincipal(principal, roles);

I use Jetty with maven, this is may configuration:
 <plugin>  
      <groupId>org.mortbay.jetty</groupId>  
      <artifactId>jetty-maven-plugin</artifactId>  
      <version>${jetty.version}</version>  
      <configuration>  
           <loginServices>  
                <loginService implementation="org.eclipse.jetty.security.HashLoginService">  
                     <name>MyRealm</name>  
                     <config>${basedir}/src/main/resources/jetty-realm.properties</config>  
                </loginService>  
           </loginServices>  
           <scanIntervalSeconds>1000</scanIntervalSeconds>  
           <webAppConfig>  
                <serverClasses>  
                     <serverClass implementation="java.lang.String">  
                          -org.eclipse.jetty.  
                     </serverClass>  
                </serverClasses>  
           </webAppConfig>  
      </configuration>  
 </plugin>  

martedì 5 giugno 2012

Tell your browser to ask for save password in web AJAX login

Store password in browser is a useful functionality, the browser intercept the POST action of form and under a few condition (browser dependent) ask to store password.

Unfortunately, this mechanism don't works well when you implement a RIA (Rich Internet Application) and use AJAX call for authenticate users because your submit through XHR and not by html form submit.

I use a simple trick to show password store request from browser. After the AJAX login request complete, if the user is successful authenticate, submit the login form to a url that return a HTTP 204 status (204 NO CONTENT). The browser ask to save password but the user does not navigate anywhere else.

This is an example:

         <form method="post" action="http://demo.utilities-online.info/backend/none">  
           Username: <input id="username" type="text" name="username"><br/>  
           Password: <input id="password" type="password" name="password"><br/>  
           <input type="button" value="login" onclick="sendRequest();">  
         </form>  

try now!

Username:
Password:


Enjoy with AJAX!