Ajax Based Login with ZK and Spring Security System

Henri Chen, Principal Engineer, Potix Corporation
October 13, 2008

Introduction

This is the second article in a series regarding how to make Spring Security 2.0 work with ZK Ajax framework. In the previous article, we only discussed about protecting traditional page-based Web applications. In this article, we will focus on how to deal with Ajax kind of application.

Like the previous article, I will focus on the steps to "make it work". If you are interested in the behind-the-scene things, you are welcome to check the source codes :-).

Demo

 

As you can see in the demo, the login window is popped up automatically rather than change to a separate login page. The end user does not have to leave the working "Accounts" page so make it more intuitive and interactive.

The Example

This is the same example as used in the previous article. It is origined from the tutorial sample(spring-security-2.0.3/dist/spring-security-samples-tutorial-2.0.x.war) provided by Spring Security 2.0. Basically I rewrote only the /WEB-INF/jsp/listAccounts.zul to handle the onClick event of -$20, -$5, +$5, and +$20 buttons. Whenever the end user presses any of these buttons, an associated onClick event listener is called and executed. The ZK event processing interceptor then intercept such event and pop up the login window if necessary.

A Minimal <zk-event> Configuration

ZK adopts the Spring namespace configuration mechanism so all you need to do to enable the ZK event processing security is as simple as following:


/WEB-INF/applicationContext-security.xml

<!--
  - Spring namespace-based configuration
  -->
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:zksp="http://www.zkoss.org/2008/zkspring"
    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
        http://www.zkoss.org/2008/zkspring
        http://www.zkoss.org/2008/zkspring/zkspring.xsd">
		
    <http ...>
        ...
    </http>
    ...      

    <!--
      - Secure the ZK event processing per the event name and ZK component path pattern
      -->
    <zksp:zk-event login-template-close-delay="5">
        <zksp:intercept-event event="onClick" path="//**/btn_*" access="ROLE_TELLER"/>
        <zksp:intercept-event path="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
    </zksp:zk-event>
    
</beans:beans>
  1. xmlns:zksp="http://www.zkoss.org/2008/zkspring" tells the Spring Security engine that we will use the ZK Spring namespace configuration and define the name space to zksp.
  2. http://www.zkoss.org/2008/zkspring http://www.zkoss.org/2008/zkspring/zkspring.xsd tells the Spring Security engine where to find such ZK Spring namespace configuration schema.
  3. <zksp:zk-event> tells the Spring Security engine we want to secure ZK event processing. This will configure the necessary listeners , filters, and Spring beans automatically. In this example, login-template-close-delay="5" tells the ZK to CLOSE the login window automatically in five(5) seconds if login sucessfully; zero(0) means close the login window immediately; and a negative value means wait for users operation.
  4. <zksp:intercept-event event="onClick" path="//**/btn_*" access="ROLE_TELLER"/> tells the Spring Security engine which ZK event and components we want to secure. In this example, it says that any onClick event that is fired to the ZK components whose id starts with btn_ will be checked againt the ROLE_TELLER authority.
  5. <zksp:intercept-event path="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/> tells that all anonymouse users can access to all events and all components.
  6. Note that the authorization follows the convention of the Spring Security system. That is, the <zksp:intercept-event> rules are checked one by one from top to bottom. As long as one rule is matched, the system will just stop there and leave, so more specific rule shall be put on upper place.
  7. Also, we used to ask you to configure the WEB-INF/zk.xml to handle the ZK event thread issue(see the previous article). You do NOT have to now since the <zksp:zk-event> tag will do those configuration for you automatically.
  8. Do remember to deploy the new ZK Spring Integration library -- zkspring.jar in WEB-INF/lib because all these magics are done by this new jar file.

Rewrite /WEB-INF/jsp/listAccounts.zul

This is the page that I domonstrate how to secure ZK event processing. It mainly lists all accounts and end users can change the balance of each account by pressing the four buttons to minus or plus the balance. In its original implementation, when the end user presses a button, a request is posted to the /post.html action page and calls the bankServer.post() method(in bigbank.BankService class) to change the account balance; then it refresh the whole page to list all accounts again.

/WEB-INF/jsp/listAccounts.zul

In this use case, the bankServer.post() method is protected by Spring Security system. If an unauthenticated user presses the button, in the original implementation, the Spring Security will show a seperate login page(and leave the current Accounts page) and ask the user to login. After sucessfully login, the security system redirect browser to the original page.

Here I rewrote this page and make it Ajaxified. When the end user presses the button, the onClick event listener is called and ONLY the changed account balance is updated. There is no need to refresh the whole page. Then how does ZK ask the end user to login without leave the current page? It pops up a login window and user can login in the popped up login window as you have seen in the demo video.


/WEB-INF/jsp/listAccounts.zul

<?variable-resolver class="org.zkoss.spring.DelegatingVariableResolver"?>
<zk>
<window title="Accouts" border="normal" width="500px">
    <zscript><![CDATA[
       void adjBalance(Button btn) {
           double bal = new Double((String)btn.getAttribute("bal")).doubleValue();
           //get the account object
           bigbank.Account a = bankService.readAccount(btn.getAttribute("aid"));
           //change the account balance
           bankService.post(a, bal);
           //update the account balance on the browser
           btn.getFellow("bal_"+a.getId()).setValue(""+a.getBalance());
       }
    ]]>
    </zscript>
    <grid>
        <rows>
            <row forEach="${accounts}">
                <label value="${each.id}"/>
                <label value="${each.holder}"/>
                <label id="bal_${each.id}" value="${each.balance}"/>
                <button id="btn_m20_${each.id}" label="-$20" onClick="adjBalance(self)">
                    <custom-attributes aid="${each.id}" bal="-20"/>
                </button> 
                <button id="btn_m5_${each.id}" label="-$5" onClick="adjBalance(self)"> 
                    <custom-attributes aid="${each.id}" bal="-5"/>
                </button> 
                <button id="btn_p5_${each.id}" label="+$5" onClick="adjBalance(self)"> 
                    <custom-attributes aid="${each.id}" bal="5"/>
                </button>  
                <button id="btn_p20_${each.id}" label="+$20" onClick="adjBalance(self)"> 
                    <custom-attributes aid="${each.id}" bal="+20"/>
                </button> 
            </row>
        </rows>
    </grid>
</window>
<button label="Home" href="/index.zul"/>
<button label="Logout" href="/j_spring_security_logout"/>
</zk>

Here I brief a little bit the process sequence of this use case:

  1. The end user clicks a button and the onClick event is fired to the button.
  2. The ZK event processing security system checks whether the event is protected(<intercept-event>) from top to bottom.
  3. If the event matches the specified event("onClick") and the component id matches the specified path(//**/btn_*, starts with btn_), the ZK event processing has to be secured.
  4. Check if the user is authenticated and with granted authority (ROLE_TELLER).
  5. Well, the user is not authenticated, pop up the login window.
  6. The end user keys in proper user id and password and submit the login form.
  7. The Spring Security system processes the authentication and succeed.
  8. The login window shows the login successful page.
  9. Since the system is configured to close the login window in 5 seconds after the user logining successfully, it starts to count down and finally close itself.
  10. The end user clicks the button again and this time it passes the authenticaltion check and proceeds to adjBalance() method.
  11. Inside the adjBalance method, we call bankService.post() method to update the account balance. Note we can refer the bankService Spring bean directly becuase we have specified in the page to use the Spring bean resolver (<?variable-resolver class="org.zkoss.spring.DelegatingVariableResolver"?>.
  12. Then we update the account balance label (without "refresh the whole page").

Summary

We have demonstrated how easy it is to secure the ZK event processing. All you have to do is declaring <zk-event> configuration tag in Spring's security configuration file. If you are not familiar with ZK event, it is OK. You can still secure the service layer methods by the Spring's @Secured annotation. As long as the method is called inside ZK event listener, the ZK event processing security system will handle it automatically.

Making ZK and Spring Security 2.0 works together is just the first step. The ZK Team will continue on making integrating ZK and Spring Framework as easy as possible. Currently ZK can refer the Spring bean easily with the varible resolver mechanism but the Spring bean has not yet been able to "inject" ZK components in any way. We will focus on solving this issue in the future and we welcome your feedback and suggestions so we can make ZK Spring Integration better.

Download

Version

Applicable to ZK Spring Integragion Library 1.0.0 (zkspring.jar) or later.

Applicable to ZK 3.0.9 and later.

Applicable to ZK 3.5.1 and later.

Applicable to Spring Security 2.0+

Download the example codes(.war file).

Comments
 
cobra
2008-10-15

期待Spring bean注入ZK components !

Marcos de Sousa
2008-10-15

Great Henri,

Always doing big staff.

It really is cool.

Eric
2008-10-17

Big job.
I have a problem.
I do not know how the application jump to he login page when the session time out in zk application.

Hong
2008-10-20

I should try...

Alex
2008-10-22

Any idea about how to catch Authentication Errors? Wher I put wrong credentials, I just get a new login page..

YADA
2008-10-24

Very cool feature. But. What about internationalization of login page?
As i can see, it is hard coded. No way to replace English text to whatever I need. Except rewrite code in zkplus package. Maybe i missed some point?

henrichen
2008-10-24

Alex and YADA,

The Ajax-Based login actually popup an ZK window and then "bridge" the login process back to Spring security's <http> login process. So you can just change the login form in the <http> configuration and do whatever you like to do.

Ian Hayes
2008-10-28

There is a GUI design problem with the security approach you outline.

Showing a user GUI options which they can select but may not be able to action/complete is generally viewed as bad design.

Hence, a better approach is work out in advance what authorization rights a user has and then change the GUI display to hide the various options they do not have rights to action.

Horace
2008-11-19

It's really cool!!
But I have a problem here. When the user's session time out or rebuild the application, it doesn't pop up the window but enter a new page. Have any idea to solve this problem??

henrichen
2008-11-20

Horace,

When the session is time out, the ZK desktop is gone, too (desktop is associated with session). It has to be start from a new page in such case.

/henri

YADA
2008-11-28

After some html form based login test, this ajax based login approach seems to be less useful. In my real secure application the login will be the next sequnce:

Unauthenticated user will go to webpage with plain http request (most users do this), user clicks on login (or try go to secure page by some other way).
Springsecurity will always redirect to a really secure login page with https request, and this will result in new login page. Almost nobody will go directly to the start page with https request, just by http.

Why https? Login page secured by http to https redirection when login page needs to show. So plain text form based login/password parameters never sent on http protocol.

Thus the usual login sequnce will result no ajax based login window.

mtthws
2009-03-11

Can you put zkspring up on maven? Also is there any good documentation on what all of the tags that the zkspring xsd offers do and how to use them any where?

mtthws
2009-03-11

I found http://docs.zkoss.org/wiki/Spring which looks like it documents a lot of the functionality.

I tried to use the ui-lookup element which is in the xsd on the web http://www.zkoss.org/2008/zkspring/zkspring.xsd, but does not appear to be in the xsd in zkspring.jar version 1.1.0. Does the schema on the web reflect current development of zkspring?

whg
2009-06-05

cobra,你和我想法一样!怎样在component内用Spring注入bean?
can we inject bean from spring applicationContext.xml to ZK component?

Slava
2009-08-24

Could you give the example when value is changed after login. Next scenario:
1. Click -$20
2. Login form
3. Login, Submit Query
4. value = -$20 (whithout additional clicking on the -$20 button)

Celso
2009-10-01

I want to see the same as Slava wrote ;-)

aaa
2009-10-31

aaaa

Bence
2009-12-04

Hi

What if I want to use the zk-style (with popup window) login and then redirect the page to a secure url, or just reload? I tried theese, but none of them did the trick:

<toolbarbutton label="Admin login" href="./admin/index.zul" />
<toolbarbutton id="btn_login" label="Admin login" href="./admin/index.zul" />
<button id="btn_login1" label="Login" onClick="sendRedirect("./admin/index.zul");"/>
<button id="btn_login2" label="Login" onClick="sendRedirect(null);"/>
<button id="btn_login3" label="reload">
   <attribute name="onClick"><![CDATA[
   Executions.sendRedirect(null); ]]>
   </attribute>
</button>
<button label="redirect">
   <attribute name="onClick"><![CDATA[ 
   Executions.sendRedirect("./admin/index.zul"); ]]>
   </attribute>
</button>

I did not found any related attributes for <zksp:zk-event> neither.

An other thing: I found this article about using ssl with spring security: http://mattfleming.com/node/269, but I could not get it work with zk.

I put the login.zul to '/secure', and add the following configuration:

<bean id="channelProcessingFilter" class="org.springframework.security.securechannel.ChannelProcessingFilter">
<property name="channelDecisionManager" ref="channelDecisionManager"/>
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON			 
PATTERN_TYPE_APACHE_ANT
/secure/**=REQUIRES_SECURE_CHANNEL
</value>
</property>
</bean>

Any tips?
Thanks

Bence
2009-12-07

Hi

It seems that my first issue is the same as the one that Slava and Celso wrote:
the original action is being lost somewhere during the authentication.

Is there a way to store the original action during authentication and then run it if authenticated?

 
 
Leave a Reply
 
Name (required)
Mail (will not be published) (required)
Website
(Case Insensitive)
Bold textItalic textUnderLine textSource CodeHorizontal rulerExternal Link
Post
Preview