Monday, June 16, 2008

Integrating Spring Security with JSF/Facelets using acegi-jsf

Abstract

Spring Security is the security framework which is a part of Spring framework technology stack. It is a mature, powerful and flexible security solution for enterprise applications. Earlier, it was widely known as Acegi Security and now it is called Spring Security. Recently I have been working on a product of mine and I currently use JSF/Facelets and Spring Webflow /Spring Security and JPA technologies to build the product. Spring webflow out of the box integrates with Spring Security and provides access to implicit expression language ( EL) variable called currentUser to get access to the authenticated principal name. I wanted more than this in other pages that are not part of Spring webflow related.

Authentication Tag Libraries

Spring Security out of the box provides the following tag support to get the authenticated principal in JSP fragments.


<security:authentication property="principal.username"/>

However, Spring Security does not provide any support of using similar tags in JSF/Facelets view technology. I did some research and found the following weblog

My big thanks to Çağatay Çivici who showed us a way to use "acegijsf" tag in JSF/Facelets. However, the website need some more fine grained details as to how to wire everything together to make use of his library. So it is my humble attempt at explaining as to how to use his library in a less invasive way. I hope this will help any novice JSF/Facelets developer to make use of tag support in JSF/Facelets view technology.

Detailed Steps

NOTE: The above said jar is customized to use Spring Security 2.0.2 instead of Acegi Security. I also added acegijsf.taglib.xml inside META-INF folder of the jar. The one you download from Sourceforge.net does not have any of these customizations.

  • Place the above said jar in WEB-INF/lib of your JSF/Facelets web application.
  • Please add the following xml fragments into your faces-config.xml located under WEB-INF of your web application.

<component>
<component-type>net.sf.jsfcomp.acegijsf.Authorize</component-type>
<component-class>net.sf.jsfcomp.acegijsf.Authorize</component-class>
</component>
<component>
<component-type>net.sf.jsfcomp.acegijsf.Authentication</component-type>
<component-class>net.sf.jsfcomp.acegijsf.Authentication</component-class>
</component>
Now, you have to add the following namespace to your facelet .XHTML
  • e.g. xmlns:acegijsf="http://sourceforge.net/projects/jsf-comp/acegijsf"
  • Thats it. Now you can utilize the following tags inside your XHTML.
 <acegijsf:authorize ifAllGranted="ROLE_ADMIN">
Add the components that are only visible to the users that satisfy the requirements here.
</acegijsf:authorize>
The attribute names are same both in jsp tag and the jsf component. You just give a role list seperated with a comma(Whitespaces omitted). All of these attributes can be bound to a value using EL.

ifAllGranted = User must be in all of the roles
ifAnyGranted = User must be in any of the roles
ifNotGranted = None of the roles must be granted for the user

This component does not render the secured children components if the user does not satisfy the granting requirements given with the attributes.

22 comments:

Anonymous said...

Great Work. It works like charm.
I am still trying to figure out how to make the following scriplet work in JSF/Facelets xhtml file

Your principal object is....: = request.getUserPrincipal()

Any pointers/suggestions will be greatly appreciated

Vigil Bose said...

try <acegijsf:authentication operation="username"/>

The above said tag will display the autenticated username similar to java.security.Principal object containing the name of the current authenticated user.

Jose Noheda said...

It works great once you're authenticated but before I'm receiving a NPE when trying <security:authentication operation="username" />. Here's the trace:

java.lang.NullPointerException
at org.apache.myfaces.shared_impl.renderkit.html.HtmlResponseWriterImpl.write(HtmlResponseWriterImpl.java:574)
at net.sf.jsfcomp.acegijsf.Authentication.encodeBegin(Authentication.java:51)
at org.ajax4jsf.renderkit.RendererBase.renderChild(RendererBase.java:280)
at org.ajax4jsf.renderkit.RendererBase.renderChildren(RendererBase.java:262)
at org.ajax4jsf.renderkit.RendererBase.renderChild(RendererBase.java:284)
at org.ajax4jsf.renderkit.RendererBase.renderChildren(RendererBase.java:262)
at org.ajax4jsf.renderkit.html.AjaxOutputPanelRenderer.encodeChildren(AjaxOutputPanelRenderer.java:79)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:594)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:239)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:246)
at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:455)
at org.ajax4jsf.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:108)
at org.ajax4jsf.application.AjaxViewHandler.renderView(AjaxViewHandler.java:189)
at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:41)
at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:140)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:152)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1081)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1016)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:145)
at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:154)
at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:260)
at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:366)
at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:493)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:190)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:130)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain._doFilter(WebAppFilterChain.java:87)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:771)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:679)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:546)
at com.ibm.ws.wswebcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:478)
at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.forward(WebAppRequestDispatcher.java:321)
at com.ibm.ws.webcontainer.extension.DefaultExtensionProcessor.handleRequest(DefaultExtensionProcessor.java:581)
at com.ibm.ws.wswebcontainer.extension.DefaultExtensionProcessor.handleRequest(DefaultExtensionProcessor.java:113)
at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:3391)
at com.ibm.ws.webcontainer.webapp.WebGroup.handleRequest(WebGroup.java:267)
at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:811)
at com.ibm.ws.wswebcontainer.WebContainer.handleRequest(WebContainer.java:1455)
at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:115)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:458)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation(HttpInboundLink.java:387)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.ready(HttpInboundLink.java:267)
at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:214)
at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:113)
at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:165)
at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
at com.ibm.io.async.AsyncChannelFuture.fireCompletionActions(AsyncChannelFuture.java:161)
at com.ibm.io.async.AsyncFuture.completed(AsyncFuture.java:136)
at com.ibm.io.async.ResultHandler.complete(ResultHandler.java:195)
at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:743)
at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:873)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1473)

Jose Noheda said...

Changing the offending line to

writer.write(result != null ? result : "");

Solved the problem

Tony said...

For those of you trying to make this work directly from XHTML (instead of from XHTML generated from a JSP), there's an additional step that needs to be done.

You need to add a facelet-taglib (that is a ".taglib.xml") configuration for the faces tag.

If you don't do this, the tags are ignored by JSF.

Mick said...

Can you be more specific on the ".taglib.xml" that is needed? I use *.xhtml for all my pages. But do not know what ".taglib.xml" I seem to need. Where would I get this or create this from?

Mick said...

I really need some help here please.
I can NOT get the Principal username at all.

Vigil Bose said...

Mick,

The tablib.xml is already embedded inside the jar file you can download from this site. The jar file is acegi-jsf-1.1.3.jar. The tag library xml you will find inside the META-INF folder of the jar is acegijsf.taglib.xml file. If you follow the instructions under Detailed Steps section, then you can use
<acegijsf:authentication operation="username"/> to get the principal inside the JSF page.

Anonymous said...

Thanks, this helped me out alot :)

cecchisandrone said...

Hi, i receive a NPE when I use < acegijsf:authentication operation="username"/ > and I'm not logged. How can I solve this problem using JSF/Facelets?

Anonymous said...

Hi,

Is this new customized jar file available in in maven repository?

Thanks,
Anil.

Vigil Bose said...

You should use the tag only after you logged in or authenticated. The jar is not published in maven repository.

Vigil Bose said...

An example of using the tags that displays the username only when the user has a particular role. The following tag usage displays the user only if the user has either ROLE_USER or ROLE_ADMIN roles.

<acegijsf:authorize ifAnyGranted="ROLE_USER,ROLE_ADMIN">
Welcome, <acegijsf:authentication operation="username"/> | <a href="${request.contextPath}/spring/logout" title="Logout">Logout</a>

</acegijsf:authorize>

Cecchi Sandrone said...

Thank you...I've done as you told and works ;)

Atul said...

I want the username in .xhtml file.
How I can get that?

I am trying as the the others said, but getting the same excepation.

Please help me?How to get this in .xhtml file

Atul

cecchisandrone said...

Do as Vigil said in the last comment...it works for me!

Atul said...

Thanks cecchisandrone

I am doing the same thing...cant get success.
I am using jdbc type implementation

I am passing the username and password, if they are valid then the role and username returns o.w.returns nothing.

I am checking if the user is active or not through query, if it is inactive it returns nothing not even role and the username is null so the error url shows on the same page "login.html/login_error=1"

I want that username to check wheather it is null for the inactive user to disaply the message

Atul said...

acegijsf:authorize ifAnyGranted="ROLE_USER,ROLE_ADMIN">
Welcome, acegijsf:authentication operation="username" | Logout

acegijsf:authorize

for this we need Role name, even we use

ifNotGranted = None of the roles must be granted for the user

I need that username .. even it is null

Vigil Bose said...

Atul,
What you need is an anonymous user role. Spring Security can display anonymous user name from the principal object provided, the user is granted privilege to use the page anonymously.

So define a new role something like ROLE_ANONYMOUS for the user you would like to see on the page. Then use only that role for the username using acegijsf tag (see my example).

The pages where you want authenticated user, use the real roles defined for the user in the database something like ROLE_USER or ROLE_ADMIN.

I hope this makes sense to you.

Regards,
Vigil

Atul said...

Vigil,

Thanks...
I solve the issue. What I do is, change the query, it return me the role. I check the role in acrgijsf tag.

Atul

Dominik Dorn said...

Hi,

I've created a simple jar file to easily integrate Spring Security and Facelets. You can even add it as maven dependency.

Take a look at
Using Spring Security with FaceletsGreetings,
Dominik

Dominik Dorn said...

Sorry, messed with the Link, it should be:Using Spring Security with FaceletsGreetings,
Dominik