Monthly Archives: September 2012

Custom Tags in JSF 2.0 for Shiro Authorization

Apache Shiro provide JSP Tag library. In JSP, I could use Shiro provided custom tag to determine show/hide html elements based on user’s permission. However, the private project I am working on, I use JSF with facelets and this gives me a dilemma.

  • JSP tag doesn’t meet the JSF lifecycle, so JSP tag from Shiro doesn’t work.
  • I need a conditional statement in the faclets (XHTML). Something like “If user has a permission, display this link. If user doesn’t have a permission, don’t display the link.”
  • Adding a conditional statement in the managed bean doesn’t help because I need to control the logic in the GUI rather than controller layer.
  • Applying security constraints in the web.xml do not help. It helps to prevent unauthorized navigation based on the role, but does not prevent users from clicking the link in the facelet.
  • I thought about implementing permission logic in the managed beans and add conditional statement in the faces-config.xml to navigate different pages based on the permission logic. However, similar to above reason, it is not the solution that I want to have.

Shiro doesn’t have official JSF custom tag support yet. However, I found Deluan’s blog where he created custom tag library for Shiro. His custom JSF 2.0 tag solves the issues that I have in the facelet.

To understand the JSF custom tag, I took a look at the several web sites that explains the JSF custom tag. To summarize my understanding,

  • Most of the custom tags are the UI component related custom tags.
  • Create a custom tag in an XHTML format.
  • Define custom tag details in a tag library.
  • Register the tag library in web.xml.

Above steps are fairly simple and many examples can be found in the  web.

For custom JSF tag, I followed Deluan’s code and simplify several things. Also, based on existing Shiro’s JSP tags, kept the same tag name.

  • Except Principal tag, all other tags doesn’t not need to return any UI output. For me this statement clarifies many things. Below three methods that need to be overridden.
    • encodeAll() –  If this component returns true from isRendered(), render this component and all its children that return true from isRendered(), regardless of the value of the getRendersChildren()flag.
    • saveState() – Invoked after the render phase has completed, this method returns an object which can be passed to the restoreState of some other instance of UIComponentBase to reset that object’s state to the same values as this object currently has.
    • restoreState() – Invoked in the “restore view” phase, this initialises this object’s members from the values saved previously into the provided state object.
    • For a custom component, it is recommended that I should focus on Restore View and Render Response which are the beginning and the end process. See JSF Lifecycle.
  • For other permission view control tags, they are extended from TagHandler. These classes returns true/false based on the Shiro’s SecurityUtils methods. E.g. isPermitted().
  • Define custom tag details in the tag library : shiro-face.taglib.xml. See below.
<tag>
 <tag-name>principal</tag-name>
  <component>
   <component-type>tag.PrincipalTag</component-type>
  </component>
<attribute>
 <description></description>
 <name>type</name>
 <required>false</required>
</attribute>
<attribute>
 <description></description>
 <name>property</name>
 <required>false</required>
</attribute>
<attribute>
 <description></description>
 <name>defaultValue</name>
 <required>false</required>
</attribute>
</tag>
<tag>
<tag-name>hasPermission</tag-name>
<handler-class>tag.HasPermissionTag</handler-class>
<attribute>
 <description></description>
 <name>name</name>
 <required>true</required>
</attribute>
</tag>
  • Define below custom UI component in face-config.xml.
<component>
 <component-type>tag.PrincipalTag</component-type>
 <component-class>tag.PrincipalTag</component-class>
</component>
  • At last, add custom tag library in the web.xml
<context-param>
  <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
  <param-value>/WEB-INF/shiro-face.taglib.xml</param-value>
</context-param>

Usage of Shiro custom tag in the JSF.

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:shiro="http://xproject.flightCenter/tag">
...
<shiro:hasPermission value="navigation:viewCities">
 <li><h:link value="Cities" outcome="viewCities.xhtml"/></li>
</shiro:hasPermission>

Basically above tag means that if this user has a Permission in following actions (navigation:viewCities:*).  Shiro defines behavior or actions in three sections; domain, action, instance. For Shiro’s permission, go here.

Summary

I encounter a need to have a facelet tag to determine if a user has a permission to navigate to certain page. Shiro provides only JSP tag library and Deluan’s blog provides custom JSF tag for Shiro. I tried to understand his code and eventually used in my XProject Facelet pages. In order to understand the whole process, I needed to understand,

  • Shiro Permission
  • Making custom tag in JSF
  • JSF lifecycle
Advertisements

JQuery vs. custom JS AJAX

I’d like to share my experience using custom Javascript library and JQuery for AJAX work. Also, I used gson to make JSON object from the collection. gson makes object conversion really easy. All sample codes below are not complete one. Just wanted to show you an idea of how things are working.

AJAX Javascript

  • Import Javascript in all JSP where Ajax action is happening.
  • Handling different browsers logic needs to be coded.
  • Conversion to JSON and object parse need to be coded.
/*
 * Create tye most optimal XML HTTP request
 * object for the browser.
 */
function createRequest()
{
    var v;

    try
    {
        v = new XMLHttpRequest(); //Mozilla and IE7
    }
    catch ( e )
    {
        v = createActivexXMLHttp(); //IE6 and back
    }

    if ( v == null )
        throw new Error("XMLHttpRequest not supported");

    return v;
}

function createActivexXMLHttp()
{
    var aVersions = [ "MSXML2.XMLHttp.5.0",
        "MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0",
        "MSXML2.XMLHttp","Microsoft.XMLHttp"];

    for ( var i = 0; i  0 )
        {
            body = body + "&";
        }
        body = body + escape(name) + "=" + escape(requestData[name]);
    }
    xhr.open("POST", url, true);
    xhr.setRequestHeader("Content-Type",
            "application/x-www-form-urlencoded; charset=UTF-8");
//    logger.trace("Post body:" + body);
    xhr.send(body);
}
/*
 * Converts a DOM document into XML text in a browser
 * neutral fashion.
 */
function getXMLString( doc )
{
    var xml = null;
    try
    {
        //Mozilla
        var ser = new XMLSerializer();
        xml = ser.serializeToString(doc);
    }
    catch ( e )
    {
        //IE
        xml = doc.xml;
    }

    return xml;
}

/*
 * Utility method that posts a DOM document.
 */
function ajaxPostDocument( url, doc, onLoad, data )
{
    var xhr = createRequest();

    xhr.onreadystatechange = function()
    {
        if ( xhr.readyState == 4 )
        {
            if ( xhr.status == 200 )
            {
                onLoad(xhr, data);
            }
            xhr = null; //Prevent memory leak in IE
        }
    }
    var xml = getXMLString(doc);
    xhr.open("POST", url, true);
    xhr.setRequestHeader("Content-Type",
            "application/x-www-form-urlencoded; charset=UTF-8");
    xhr.send("xml=" + escape(xml));
}

ajaxPost() function was used from JSP to call Ajax request.

Object to JSON without gson

JSONArray was used to convert objects into JSON. Also JSONWritter was used to pass other parameters from server to client.

StringWriter aWriter = new StringWriter();
JSONWriter theWriter = new JSONWriter( aWriter );
JSONArray jsonArray = new JSONArray();
JSONObject object = new JSONObject(ajaxBean, PROPERTY_LIST); 
//Simple javabean with properties and String array with properties
jsonArray.put(object);

theWriter.object().
        key( "outages" ).value( jsonArray ).        
        key( "successfulIndicator" ).value( "1" ).        
        key( "error" ).value( "" ).
        endObject();
aWriter.toString();

Parse in JSP

function displayGrid( reply )
{
  var jsonString = reply.responseText;

  if ( jsonString != null && jsonString != "" )
  {
    var tableData = eval("(" + jsonString + ")");
    if ( tableData.error != "" )
    {
      $("#error").text(tableData.error);
    }
    else
    {
      $("#error").empty();
      var outageList = tableData.outages;
      for ( var i = 0; i < outageList.length; ++i )
      {
            var outage = outageList[i];
            var cell = row.insertCell(0);
            cell.innerHTML = outage.id;
            ....
      }
    }
  }
}

Lots of JS code…

AJAX JQuery using gson

public void doPost( HttpServletRequest aRequest, HttpServletResponse aResponse )
{
   Collection lockList 
   = convertToAjaxBean(getService().getWorkProtectionJobController().retrieveAllDataLock());

   Gson gson = new Gson();
   String json = gson.toJson( lockList );
   aResponse.setContentType( "application/json" );
   aResponse.getWriter().write( output );
}

Parse JSON from JSP

function retrieveAll()
  {
    $.ajax({
      url: servletUrl,
      dataType: 'json',
      data: {Action : "retrieveAll"},
      cache: false,
      beforeSend: function()
      {
        messageBox_show();
      },
      success: function(data)
      {
        drawTable(data);
      },
      complete: function()
      {
        messageBox_hide();
      }
    });
  }

  function drawTable(data)
  {
    var table = document.getElementById("ajaxTable");
    for(var i = 0; i < data.length; i++)
    {
      var lock = data[i];
      var rowIdx = table.rows.length;
      var row = table.insertRow(rowIdx);

      var cell = row.insertCell(0);
      cell.align = "left";
      cell.className = "tips";
      cell.innerHTML = lock.objectId;
      ....
    }
}

Because JSON is not nested with another array, I could just access JSON data from JQuery ajax call on Success.

Conclusion

JQuery and gson combination makes development a lot easier.  Although some shorthanded JQuery usage is really tricky, but once I master the actual JQuery Ajax syntax, it doesn’t seem too bad to understand shorthanded format. Some situation, returned JSON object contains complex structure such as nested JSON array. I found printing out JSON and validate its format through JSONLint before parsing JSON is helpful.