Making Spring Security Work with ZK
Henri Chen,
Principal Engineer, Potix Corporation
September 15, 2008
New Article Link
For how to use Spring Security to do the authorization(using EL or other mechanism) in your Zul file, please refer to: Customize Your ZK Pages Per Spring Security Authority Roles
Introduction
Spring Security 2.0 is the next generation security system for the Spring Framework. It has added many new features since its predecessor Acegi Security System. There has been questions regarding how to make Spring Security 2.0 work with ZK Ajax framework and here is an example that demonstrates how to make it work step-by-step.
Demo
The Example
To make things simple, I borrowed the tutorial sample(spring-security-2.0.3/dist/spring-security-samples-tutorial-2.0.x.war) provided by Spring Security 2.0 . I then rewrite the original pure Jsp pages to ZK pages and add necessary ZK libraries and configurations to show you how these two frameworks can work together seamlessly. The tutorial sample demonstrates many basic concepts of the security system including form-based authentication mechanism with remember-me and annotated method authorization, etc.. I will not go to details in Spring Security's mechanism in this article. Rather, I will focus on the steps to "make it work" and demonstrate the rewritten ZK pages. If you are interested in the behind-the-scene things, you can check them on Spring Security web site.
Deploy the war file
The easist way to try the sample is to deploy the war file to your servlet container. In this article, I use Tomcat 5.5. It is simple. Just copy the file spring-security-samples-tutorial-2.0.x.war to the webapps folder of Tomcat server and restart Tomcat. The tutorial war file shall deploy and create a new folder spring-security-samples-tutorial-2.0.x (same name as the file) under folder webapps. Now visit http://localhost:8080/spring-security-samples-tutorial-2.0.x/ and you shall see following page.
/index.jsp
Configure the /WEB-INF/web.xml file
Now lets start walking through the configurations to see how to make Spring Security works with ZK web application. The /WEB-INF/web.xml is the major configuration file that tells Tomcat what to do. To make Tomcat work with Spring Security, you shall configurate it as following. These shall be already specified in the tutorial sample codes, so basically you just don't have to change any thing.
/WEB-INF/web.xml
...
<!--
- Location of the XML file that defines the root application context
- Applied by ContextLoaderListener.
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
...
/WEB-INF/applicationContext-security.xml
</param-value>
</context-param>
<!--
- Loads the root application context of this web app at startup.
- The application context is then available via
- WebApplicationContextUtils.getWebApplicationContext(servletContext).
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
- Spring Security Filter Chains
-->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
<!--
- ZK configurations
-->
<listener>
<description>Used to cleanup when a session is destroyed</description>
<display-name>ZK Session Cleaner</display-name>
<listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class>
</listener>
<servlet>
<description>ZK loader for evaluating ZK pages</description>
<servlet-name>zkLoader</servlet-name>
<servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>
<init-param>
<param-name>update-uri</param-name>
<param-value>/zkau</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.zul</url-pattern>
</servlet-mapping>
<servlet>
<description>The asynchronous update engine for ZK</description>
<servlet-name>auEngine</servlet-name>
<servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>auEngine</servlet-name>
<url-pattern>/zkau/*</url-pattern>
</servlet-mapping>
...
<welcome-file-list>
index.zul
</welcome-file-list>
- The <context-param> tells Spring Framework context loader where to find the context parameter files.
- The <listener> ContextLoaderListener defines the Spring Framework context loader that will load and prase the context parameter files defined in the
parts. - The <filter> springSecurityFilterChain defines the entry servlet filter for Spring Security filter chains.
- The <filter-mapping> tells what pages (via URL pattern) shall go through and checked by the Spring Security filter chains.
The configurations specific to Spring Security is actually the last two items(item 3 and 4). The filter name springSecurityFilterChain is important. You shall just copy them as is. The item 1 and 2 are generic configuration to make Spring Framework work. Note that the /WEB-INF/applicationContext-security.xml defined in item 1 is the Spring Security specific configuration file and you can include other business specific spring configuration files here, too. I will explain it later.
- The <listener> HttpSessionListener is used to cleanup the session when it is destroyed.
- The <servlet> zkLoader servlet is used to load a ZK page.
- The <servlet> auEngine servlet is used to update a ZK page (Ajax update).
- And you can see the <servlet-mapping> for zkLoader is specific to
*.zulpages and all Ajax XMLHttpRequest url starts with the/zkau/*pattern.
Copy library jar files to /WEB-INF/lib
After done with the /WEB-INF/web.xml, now is the turn for required library jars. For Spring Security to work, we shall copy following jars to /WEB-INF/lib.
Spring Security library jar files
aopalliance-1.0.jar
aspectjrt-1.5.4.jar
commons-codec-1.3.jar
commons-collections-3.2.jar
commons-lang.jar
commons-logging-1.1.1.jar
jstl-1.1.2.jar
log4j-1.2.14.jar
spring-security-acl-2.0.3.jar
spring-security-core-2.0.3.jar
spring-security-core-tiger-2.0.3.jar
spring-security-taglibs-2.0.3.jar
spring.jar
*spring-web.jar
*spring-webmvc.jar
standard-1.1.2.jar
*These two jar files are not required and are included because they are used in this sample example.
And the following is for ZK framework. Basically, just follow the ZK Quick Start Guide. Copy all /dist/lib/*.jar, /dist/lib/ext/*.jar, /dist/lib/zkforge/*.jar to /WEB-INF/lib
ZK library jar files
bsh.jar
commons-collections.jar
commons-fileupload.jar
commons-io.jar
commons-logging.jar
fckez.jar
Filters.jar
gmapsz.jar
groovy.jar
itext.jar
jasperreports.jar
jcommon.jar
jfreechart.jar
jruby.jar
js.jar
jxl.jar
jython.jar
mvel.jar
ognl.jar
poi.jar
timelinez.jar
zcommon.jar
zcommons-el.jar
zhtml.jar
zk.jar
zkex.jar
zkmax.jar
zkplus.jar
zml.jar
zul.jar
*zuljsp.jar
zweb.jar
*This is ZK JSP Tags libraries. We will use it in the sample codes.
Configure the /WEB-INF/zk.xml file: the ThreadLocal issue (IMPORTANT!)
The Spring Security engine holds in the servlet thread a ThreadLocal variable contextHolder for each request so the engine can refer it from time to time. This ThreadLocal variable contains important security related information and shall be accessiable any time. However, ZK by default spawns a new event thread for each event handling job. That is, the ZK event thread will not have such important contextHolder ThreadLocal variable and the original assumption is broken.
There are two ways to solve this issue. You can choose either one and configure it in the /WEB-INF/zk.xml file.
- Disable the ZK event thread mechanism entirly. This tells the ZK framework NOT to spawn a new event thread for event handling and everyting back to normal.
<system-config> <disable-event-thread/> </system-config> - Use ZK provided ThreadLocalListener utitlity to copy the
contextHolderThreadLocal variable over from servlet thread to ZK event thread and vice versa.<listener> <description>ThreadLocal Synchronization Listener</description> <listener-class>org.zkoss.zkplus.util.ThreadLocalListener</listener-class> </listener> <preference> <name>ThreadLocal</name> <value> org.springframework.security.context.ThreadLocalSecurityContextHolderStrategy=contextHolder </value> </preference>
Note that the ZK event thread mechanism is the base of the ZK modal window such as Messagebox. That is, if you disabled the event thread mechanism as said in item 1, you could not use ZK modal window, either. Choose either solution per your requirements.
Configure the /WEB-INF/applicationContext-security.xml
This file tells Spring Securty engine what to do. This might be the most important file for Spring Security system. The file uses the new simplified namespace-based configuration syntax and shrinks used to be hundreds of lines of old configuration codes into just less than 10 lines.
/WEB-INF/applicationContext-security.xml
<!--
- Spring namespace-based configuration
-->
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
<!--
- Enable the @Secured annotation to secure service layer methods
-->
<global-method-security secured-annotations="enabled">
</global-method-security>
<!--
- Secure the page per the URL pattern
-->
<http auto-config="true">
<intercept-url pattern="/secure/extreme/**" access="ROLE_SUPERVISOR"/>
<intercept-url pattern="/secure/**" access="IS_AUTHENTICATED_REMEMBERED" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<!-- use own login page rather than the default one -->
<form-login login-page="/login.zul"/>
</http>
<!--
Usernames/Passwords are
rod/koala
dianne/emu
scott/wombat
peter/opal
-->
<authentication-provider>
<password-encoder hash="md5"/>
<user-service>
<user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
<user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER,ROLE_TELLER" />
<user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" />
<user name="peter" password="22b5c9accc6e1ba628cedc63a72d57f8" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</beans:beans>
<global-method-security secured-annotations="enabled">tells the Spring Securty engine that we will use @Secured Java annotation to secure service layer methods.<http auto-config="true">tells the Spring Security engine to use the default configured security filter chains and services. This single line covers 90% of the configurations.- The
<intercept-url pattern="..." access="...">tells what pages (per the URL pattern) can be accessed by what user roles. The most specific pattern shall be put at the top since Spring Security engine checks one by one until the URL is matched with the pattern. - The
<form-login login-page="/login.zul"/>here tells that we want to use the specified /login.zul page as the user login form page. If you did not specify this, the security system will use its internal one automatically.
- The
- The
<authentication-provider>gives an in-memory user/password list. This is generally used for testing and simple cases. You can implement your own user/password provider mechanism per your requirment in different conditions (e.g. use database, etc.). Note that the long integer password="xxxxxxxxxxx..." is the md5 result of the password string that avoids passing plain text across the Internet.
Until here, it is the end of the configuration. In following case, I will rewrite orignal JSP pages to ZK pages and show you how ZK and Spring Security work together.
Rewrite /index.jsp to /index.zul
This is easy. I lay out the screen with <grid> components and use <button href="..."> for hyperlinking to other pages. Compare it with original index.jsp shown above.
/index.zul
/index.zul
...
<grid>
...
<rows>
<row>
Any one can list accounts.<button label="Go!" href="listAccounts.html"/>
</row>
<row>
Your principal object is ...:<label value="${desktop.execution.userPrincipal.name}"/>
</row>
<row>
Secure page<button label="Go!" href="secure/secure.jsp"/>
</row>
<row>
Extremely secure page<button label="Go!" href="secure/extreme/extreme.jsp"/>
</row>
</rows>
</grid>
...
This page is the entry page for this sample example. The button in first row will list all accounts. The second show the currently login user name(empty if not login yet). Press the button in third row will access a secure page whilst press the button in last row will access an extreme secure page(need ROLE_SUPERVISOR permission).
Rewrite /login.jsp to /login.zul
This is the customized login page. Spring Security will show this login page when it is necessary. Again, I lay out the screen with grid and mix html <h:form> , <h:input ...> and ZK components together. This is a typical practice to mix ZK components with legacy servelet and form-based page. For details, you can refer this article. A normal login page will definitly not have the "Valid users" part. This is a tutorial example... In this example, the user rod is the one with ROLE_SUPERVISOR permission, dianne is the one with ROLE_TELLER permission, and the other two are with ROLE_USER permission. You can check /WEB-INF/applicationContext-security.xml for details. Test the example to see how each user can access each page.
/login.zul
/login.zul
...
<h:form id="f" name="f" action="j_spring_security_check" method="POST"
xmlns:h="http://www.w3.org/1999/xhtml">
<grid>
<rows>
<row>User: <textbox id="u" name="j_username"/></row>
<row>Password: <textbox id="p" type="password" name="j_password"/></row>
<row>
<checkbox id="r" name="_spring_security_remember_me"/>
Don't ask for my password for two weeks
</row>
<row spans="2"><hbox>
<h:input type="submit" value="Submit Query"/>
<h:input type="reset" value="Reset"/>
</hbox></row>
</rows>
</grid>
</h:form>
...
Rewrite /WEB-INF/jsp/listAccounts.jsp to /WEB-INF/jsp/listAccounts.zul
This page is used to list all accounts. It is shown when the user press the first button in the home page(/index.zul). If you check carefully, you will find it actually visits /listAccounts.html. Why visiting an .html page at root turn out responding with a .zul page inside /WEB-INF/jsp/. This has something to do with the forwarding mechanism embedded in Spring MVC. If you are interested, please check the /WEB-INF/bank-servlet.xml, /src/bigbank/web/ListAccounts.java, and /src/bigbank/BankService.java source files. This is not directly related to the Spring Security so I will just brief a little bit.
- The request for
/listAccounts.html - Per the difinition in
/WEB-INF/bank-servlet.xml, thehandleRequestmethod of theListAccountscontroller is called. - And inside that method it calls
bankService.findAccounts()to populate a variableaccounts. - At
bankService.findAccounts(), we can use Spring Securtity annotation@Securedto protect the calling of that method. Here we set it toIS_AUTHENTICATED_ANONYMOUSLYso any one can access this method. - Then the url is forwarded to
/WEB-INF/jsp/listAccounts.zulper the pattern definition in/WEB-INF/bank-servlet.xml
As for the listAccounts.zul, you can see that we then iterate the accounts collection to show each Account as a <row> in the <grid>.
/WEB-INF/jsp/listAccounts.zul
/WEB-INF/jsp/listAccounts.zul
...
<grid>
<rows>
<row forEach="${accounts}">
<label value="${each.id}"/>
<label value="${each.holder}"/>
<label value="${each.balance}"/>
<button label="-$20" href="post.html?id=${each.id}&amount=-20.00"/>
<button label="-$5" href="post.html?id=${each.id}&amount=-5.00"/>
<button label="+$5" href="post.html?id=${each.id}&amount=5.00"/>
<button label="+$20" href="post.html?id=${each.id}&amount=20.00"/>
</row>
</rows>
</grid>
...
In this page, end users can press the "-$20", "-$5", "+$5", "+$20" button to add/minus the total amount of each Account and the updated value would be refreshed after. How does this work? Take a look of the code <button ... href="post.html?id=..."/>. This is another place that Spring MVC get involved. Check the /WEB-INF/bank-servlet.xml, /src/bigbank/web/PostAccounts.java, and /src/bigbank/BankService.java source files for details if you are interested.
- The request for
"post.html?id=..."when the button is clicked. - Per the difinition in
/WEB-INF/bank-servlet.xml, thehandleRequestmethod of thePostAccountscontroller is called. - And inside that method it calls
bankService.readAccount()andbankService.post()to modify the account amount. - At
bankService.readAccount()andbankServer.post(), we use Spring Securtity annotation@Securedto protect the calling of those two methods. Here thebankServer.post()is protected by ROLE_TELLER permission. That is, only user with ROLE_TELLER permission is allowed to call this method. - Then the url is redirect back to
/listAccounts.htmlas required in thehandleRequestmethod of thePostAccountscontroller. - The
/listAccount.htmlis then refreshed as we have described.
Rewrite /secure/index.jsp
/secure/index.jsp
/secure/index.jsp
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ taglib prefix="z" uri="http://www.zkoss.org/jsp/zul" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html>
<body>
<z:page>
<z:window title="Secure Page" border="normal" width="500px">
<p>
This is a protected page. You can get to me if you've been remembered,
or if you've authenticated this session.
</p>
<sec:authorize ifAllGranted="ROLE_SUPERVISOR">
You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br/><br/>
</sec:authorize>
<h4>Properties obtained using <sec:authentication /> tag</h4>
<z:grid>
<z:columns>
<z:column label="Tag"/>
<z:column label="Value" width="50px"/>
</z:columns>
<z:rows>
<z:row><z:label value="<sec:authentication property='name' />"/><sec:authentication property="name"/></z:row>
<z:row><z:label value="<sec:authentication property='principal.username' />"/><sec:authentication property="principal.username"/></z:row>
<z:row><z:label value="<sec:authentication property='principal.enabled' />"/><sec:authentication property="principal.enabled"/></z:row>
<z:row><z:label value="<sec:authentication property='principal.accountNonLocked' />"/><sec:authentication property="principal.accountNonLocked"/></z:row>
</z:rows>
</z:grid>
<z:separator bar="true"></z:separator>
<z:button label="Home" href="../"/>
<z:button label="Logout" href="../j_spring_security_logout"/>
</z:window>
</z:page>
</body>
</html>
This page is a little bit special. The original JSP uses some Spring Security <sec:...> tags to control the visibilities of some parts of this page and fetch security related information. I have to preserve these <sec:...> functions whilst still using ZK components. How do I make it? I use the ZK JSP Tags to handle this case. ZK JSP Tags provides a straightforward way of enriching legacy JSP pages and can be integrated seamlessly with other JSP tags and solutions.
- The
<!DOCTYPE ...>is important for Internet Explorer browser. Just copy as is. - The
<%@ taglib prefix="z" uri="http://www.zkoss.org/jsp/zul" %>is the declaration for ZK JSP Tags. I generally prefix it with "z". - All other ZK <z:...> component tags used must be enclosed in a
<z:page>tags. - And you can mix other JSP tags and HTML tags inside without problems.
As you can see, this is probably the most easy way to enrich JSP pages.
Back to the Spring Security itself. The <sec:authorize ifAllGranted="ROLE_SUPERVISOR"> tag tells the Spring Security engine that until the user is granted with ROLE_SUPERVISOR permission, the enclosed content "You are a supervisor! ..." is not rendered. And the <sec:authentication property="principal.username"> would fetch the username of currently login principal, etc. There are other useful security tags. Check the Spring Security web site for details.
Rewrite /secure/extreme/index.jsp
/secure/extreme/index.jsp
This page is protected and only user with ROLE_SUPERVISOR permission is allowed to access it. It like the /secure/index.jsp uses some Spring Security <sec:...> tags. Again, I use the ZK JSP Tags to enrich it with ZK components.
/secure/extreme/index.jsp
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ taglib prefix="z" uri="http://www.zkoss.org/jsp/zul" %>
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %>
<html>
<body>
<z:page>
<z:window title="VERY Secure Page" border="normal" width="500px">
This is a protected page. You can only see me if you are a supervisor.
<authz:authorize ifAllGranted="ROLE_SUPERVISOR">
You have "ROLE_SUPERVISOR" (this text is surrounded by <authz:authorize> tags).
</authz:authorize>
<z:separator bar="true" spacing="10px"/>
<z:hbox>
<z:button label="Home" href="../../"/>
<z:button label="Logout" href="../../j_spring_security_logout"/>
</z:hbox>
</z:window>
</z:page>
</body>
</html>
Summary
Spring Security 2.0 is a powerful security system for Web applications. It secures the URL pages and service layer method calls with very simple configurations and annotations. ZK pages can be easily protected by the Spring Security system and ZK JSP Tags provides a straghtforward way for you to mix ZK tags and Spring Security tags together seamlessly.
In this article we only discuss about traditional page-based Web applications. That might not mean anyting if we cannot deal with Ajax kind of applications. For example, in the middle of a working page, if an end user is requested to login half way, it is better to pop up a login window rather than show anthor login page and force the end user to leave the current working one. To achieve such interactive and responsive way of handling authentications and securities, the ZK framework and Spring Security system would need more cooperations. I will talk about that in the next article regarding this topic.
Download
Version
Applicable to ZK 3.0.8 and later.
Applicable to ZK 3.5.0 and later.
Applicable to Spring Security 2.0+
Download the example codes(.war file).







