From Documentation

(Difference between revisions)
Jump to: navigation, search
(ZK Ajax Request Security Handling)
m (ZK Ajax Request Security Handling)
Line 209: Line 209:
}
}
</source>
</source>
 +
The ''AccessDeniedException'' is a key to Spring Security's filter chain to handle, and everything works fine during a page request situation, but what if we throw the exception in an action say ZK's ''EventListener''?
-
===  Security Check in ZK's Action ===
+
==  Security Check in ZK's Action ==
 +
 
 +
Without any further adapting code, throwing ''AccessDeniedException'' during an Ajax request won't trigger Spring Security's authentication and authorization process, that's because in the ZK AU Engine's perspective, it's impossible for any others outside the ''org.zkoss.zk.au.http.DHtmlUpdateServlet'' that could know how to compose a meaningful and correctly-structured response to the client engine at the browser. So any thrown exception must be handled by ZK.
 +
 
 +
To over come this, we have to convert ZK's Ajax request to a normal page request, and we can then let Spring Security handle the ''AccessDeniedException''. the workaround include 3 steps:
 +
 
 +
=== STEP 1: Convert Ajax Request to Page Request ===
 +
 
 +
=== STEP 2: Throw AccessDeniedException to adapt Spring Security Process ===
 +
 
 +
=== STEP 3: Handle the Login Success Redirection ===
=Summary=
=Summary=

Revision as of 07:44, 27 February 2013





  • Author
    Ian Tsai, Engineer, Potix Corporation
  • Date
    March 04, 2013
  • Version
    ZK 6.5.X, Spring Security 3.1.2

Under Construction-100x100.png This paragraph is under construction.

Contents

Introduction

Spring Security is a common solution for developer to serve security needs in a Java web application, it is widely used and is a proven technology. However, due to its nature to protect resources by pattern matching through URL, it's not that obvious for application developer to delegate Spring Security with general amount of ajax frameworks which use Json format in form to structure meta-information.

So in this article, I'll introduce how to integrate Spring Security with ZK seamlessly by going through the construction of a simple demo application(An article publish and editing system).

Resources to Download

I use Git as my source control and has stored my code at Github, you can check out the demo project from here.

If you are more comfortable to war format, you can get the archived project here.

The project is based on Maven, if you want to try different version of ZK or Spring, please change the version number in pom.xml

Demo Application Details

This demo application is a simple article publish and edit system which allows three kinds of user to access: user with role ROLE_USER, user with role ROLE_EDITOR and the anonymous user with default reserved role IS_AUTHENTICATED_ANONYMOUSLY.

Spring security integration use case diagram.png
  • Anonymous user can visit the homepage which contains article list, and they can view an article's content by clicking it's link in homepage.
  • User with role ROLE_USER is allowed to post new article and edit their own articles.
  • User with role ROLE_EDITOR is most powerful user who is able to edit and delete any article.

This article will based on the implementation requirements of this application to demonstrate the integration of Spring Security and ZK.

Spring Security Configuration

First, let's see how to configure our project. To use Spring Security, we have to add some listener and filter declarations in web.xml:

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/applicationContext.xml 
			/WEB-INF/applicationContext-security.xml
		</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>
	<listener>
		<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
	</listener>
	<filter><!-- the filter-name must be preserved,  do not change it! -->
		<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>

As you can see, despite those ordinary Spring Context Listeners(RequestContextListener and ContextLoaderListener), we declared HttpSessionEventPublisher and springSecurityFilterChain for Spring Security. Here the HttpSessionEventPublisher is optional and it is designed for Spring Security to do detailed concurrent session control, the springSecurityFilterChain is the main hook for all Spring Security's functionality to the application, it's required and must be named springSecurityFilterChain.

applicationContext-security.xml

Here in this project, I separated Spring's ApplicationContext.xml to two files, the original ApplicationContext.xml is for backend bean and sevice bean declarations, and the additional applicationContext-security.xml is for Spring Security's configuration only.

In applicationContext-security.xml, there are two major elements we have to setup, the <http> element and the <authentication-manager> element.

Http Element Setting

The <http> element is to tell Spring what kind of resources need to be secured, which port will be used by container for http & https connections, and what kind of log-in solution will be used in this web application.

<http auto-config="true">
	<port-mappings>
		<port-mapping http="8080" https="8443"/>
	</port-mappings>
	
	<intercept-url pattern="/zkau/**" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="any"/>
	<intercept-url pattern="/login.zul" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="https" />
	<intercept-url pattern="/newArticle.zul" access="ROLE_USER" requires-channel="https" />
	<intercept-url pattern="/j_spring_security_check" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="https" />
	<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="any" />
		
	<session-management session-fixation-protection="none" /> 
		 
	<form-login login-page="/login.zul" 
		authentication-failure-url="/login.zul?login_error=1" 
		login-processing-url="/j_spring_security_check"/>
		
	<logout logout-success-url="/index.zul" invalidate-session="true" />
</http>

Authentication Manager Setting

The <authentication-manager> element declaration is designed to manage authentication-provider which is the provider of Spring's authentication instance and will do the real work of the authentication. You can declare multiple authentication-providers in order to leverage different source of users(ex: openidAuthenProvider or LDAPAuthenProvider), in this project we extends UserDetailsService from default authentication-provider:

<authentication-manager>
	<authentication-provider user-service-ref="myUserDetailsService">
		<password-encoder hash="md5" />
	</authentication-provider>
</authentication-manager>
	
<beans:bean id="myUserDetailsService" 
	class="org.zkoss.demo.springsec.model.MyUserDetailsService"/>

Protecting a Page Request

After the configuration of Spring Security, now, let's see how to use it to protect a zul file request.

Scenario: When User requesting a restricted resource

In our demo project, NewArticle.zul is a restricted resource that can only be accessed by user who already logged in, it's natural because to post a new article, we have to know who is the author. In Spring Security, to restrict a resource for certain user( who has enough permission) to access is very simple, we simply declare <intercept-url> element under <http> in applicationContext-security.xml:

<intercept-url pattern="/newArticle.zul" access="ROLE_USER" requires-channel="https" />

Here the pattern attribute is to determine which request this setting will take effect, and if a request's URL matches this pattern, the access attribute will be used to check current user's authorities.

As what we see, the page request protection under Spring Security is very straightforward and intuitive, it's all based on pattern matching to request path.

Log-in Page Implementation in ZK

Now, after the newArticle.zul has been secured, the log-in page(login.zul) which is required for authentication process has to be implemented for user to perform log in. Here let's see how to implement a log-in page in ZUL:

<html:form id="f" name="f" action="j_spring_security_check" method="POST" 
	xmlns:html="native">
    	<grid>
    		<rows>
    			<row>User: <textbox id="u" name="j_username"/></row>
    			<row>Password: <textbox id="p" type="password" name="j_password"/></row>
    			<row spans="2">
    				<hbox>
    					<html:input type="reset" value="Reset"/>	
    					<html:input type="submit" value="Submit Query"/>
    					<button type="submit" label="Accedi_trendy" mold="trendy"/>
    					<button type="submit" label="Accedi_os" />
  	  					
    				</hbox>
    			</row>
    		</rows>
    	</grid>
</html:form>
  1. An html <form> element has to be declared outside the username and password inputs. To use native html <form> element in ZUL, please refer to native namespace.
  2. The form must contains two input elements named j_username and j_passoword. To apply some ajax effect and features to these inputs, we can use ZK's <textbox> component instead.
  3. Form action attribute has to be mapped to login-processing-url attribute's value of <form-login> element in applicationContext-security.xml

Now, if an anonymous user click the New Article button in homepage, he will be redirect to our login.zul.

Securing Partial View By Using EL in ZUL

In order to secure partial view part in web page, Spring Security has its own taglib which provides basic support for accessing security information and applying security constraints in JSPs(You can find the implementation in the source code of spring-security-taglibs-3.1.2.RELEASE.jar). And here for security in ZULs, ZK framework also provides approaches to define new tag library. In our demo application, for using Spring Security through EL we made our own tag library.

Zul Tag Library for Spring Security

Though ZK Developer Reference provides very details of what we can do in a custom taglib of ZK, here we only focus on what we want to have for Spring Security.

First, let's create a security.tld under /WEB-INF/. you can do the classpath:metainfo/tld/config.xml stuff as the Developer Reference mentioned, it's more suitable for a serious project.

Second, define the EL functions in /WEB-INF/security.tld, here's the example:

<taglib>
	<uri>http://www.zkoss.org/demo/integration/security</uri>
	<description>
		Methods and actions for ZK + Spring Security
	</description>

	<function>
		<name>isAllGranted</name>
		<function-class>org.zkoss.demo.springsec.SecurityUtil</function-class>
		<function-signature>
	boolean isAllGranted(java.lang.String authorities) {
		</function-signature>
		<description>
	Return true if the authenticated principal is granted authorities 
	of ALL the specified roles.
		</description>
	</function>
...

As what we can see, the document need to be started with a root element: <taglib> with an <uri> element in it. Then, you can declare functions. In the function declaration, we mapped a method(isAllGranted()) of org.zkoss.demo.springsec.SecurityUtil to our EL function isAllGranted, about the SecurityUtil, the implementation of it is based on Spring Security's SecurityContextHolder

Now let's see how to use the custom taglib of Spring Security in ZUL.

Usage: Protect Action

In our demo project, according to the use cases above, we have a "delete article" function that only user with role: ROLE_EDITOR can access. Now, with our custom taglibs for Spring Security, we are able to set up some constraints for it like this:

<?taglib uri="/WEB-INF/security.tld" prefix="sec"?>
...
<button id="deleteBtn" label="Delete"
	if="${sec:isAllGranted('ROLE_USER')}"
	disabled="${not sec:isAllGranted('ROLE_EDITOR')}"/>
...

Special ZK Component Attributes of Security

In the sample code above, I use custom EL + disabled attribute of ZK button to protect the click from user, ZK also provides other attributes that may be applicable for security constraint such as visible and readonly(for combobox). These attributes which are based on html effects are very useful to fulfill the requirements of the escalation of permission to access UI.

To be more secured and to protect the server-side access of a ZK component, please refer to this article: Block Request for Inaccessible Widgets

ZK Ajax Request Security Handling

Now, let's talk about the most difficult part of this integration, how to deal with ZK's Ajax request by using Spring Security. First, we have to take a look at how to protect a page request in a more programmatic way.

Do Security Check in ZK's Listener

In some situation, user might need to do security check in java code not in ZUL, for example, in our accessDeniedExTest.zul, we have an Initiator that will do security check in public void doInit(Page page, Map<String, Object> args) throws Exception method:

public class AccessDeniedExInit extends GenericInitiator {
	public void doInit(Page page, Map<String, Object> args) throws Exception {
		if(SecurityUtil.isNoneGranted("ROLE_EDITOR")){
			throw new AccessDeniedException("this is a test of AccessDeniedException!");	
		}
	}
}

The AccessDeniedException is a key to Spring Security's filter chain to handle, and everything works fine during a page request situation, but what if we throw the exception in an action say ZK's EventListener?

Security Check in ZK's Action

Without any further adapting code, throwing AccessDeniedException during an Ajax request won't trigger Spring Security's authentication and authorization process, that's because in the ZK AU Engine's perspective, it's impossible for any others outside the org.zkoss.zk.au.http.DHtmlUpdateServlet that could know how to compose a meaningful and correctly-structured response to the client engine at the browser. So any thrown exception must be handled by ZK.

To over come this, we have to convert ZK's Ajax request to a normal page request, and we can then let Spring Security handle the AccessDeniedException. the workaround include 3 steps:

STEP 1: Convert Ajax Request to Page Request

STEP 2: Throw AccessDeniedException to adapt Spring Security Process

STEP 3: Handle the Login Success Redirection

Summary


Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.