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
isRendered(), render this component and all its children that return
isRendered(), regardless of the value of the
- 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.
- encodeAll() – If this component returns
- 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.
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