Chapter 8: Authentication
Target Application
In this chapter, we will demonstrate how to implement authentication and protect your pages from illegal access. We will create a login page without sidebar as follows:
After login, we redirect users to index page with user name appeared at right side of the header.
Authentication
Authentication is the process to identify a user, and requesting an account and a password is a common way. Our login page also uses a template zul to keep a consistent style with index page. But it has no sidebar because users without login should not access main functions.
/chapter8/layout/template-anonymous.zul
<zk>
<!-- free to access template, without sidebar -->
<borderlayout hflex="1" vflex="1">
<north height="100px" border="none" >
<include src="/chapter8/layout/banner.zul"/>
</north>
<center id="mainContent" autoscroll="true" border="none" self="@insert(content)">
<!-- the main content will be insert to here -->
</center>
<south height="50px" border="none">
<include src="/chapter3/footer.zul"/>
</south>
</borderlayout>
</zk>
- Line 7: Define an anchor named content
Login form is built with Grid.
/chapter/8/login.zul
<?link rel="stylesheet" type="text/css" href="/style.css"?>
<!-- it is a login page, no authentication protection and use anonymous template -->
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/chapter8/layout/template-anonymous.zul"?>
<zk>
<hbox self="@define(content)" vflex="1" hflex="1" align="center"
pack="center" spacing="20px">
<vlayout>
<window id="loginWin"
apply="org.zkoss.tutorial.chapter8.LoginController"
title="Login with you name" border="normal" hflex="min">
<vbox hflex="min" align="center">
<grid hflex="min">
<columns>
<column hflex="min" align="right" />
<column />
</columns>
<rows>
<row>
Account :
<textbox id="account" width="200px" />
</row>
<row>
Password :
<textbox id="password" type="password"
width="200px" />
</row>
</rows>
</grid>
<label id="message" sclass="warn" value=" " />
<button id="login" label="Login" />
</vbox>
</window>
(use account='zkoss' and password='1234' to login)
</vlayout>
</hbox>
</zk>
- Line 3: Apply a template zul with <?init ?>.
- Line 5: Define a fragment to be inserted in the anchor content.
- Line 24: Specify "password" at type, then user input will be masked.
Understand Session
Before proceeding to implement login function, we have to understand "session" first. A web application operates over HTTP protocol which is stateless; each request and its corresponding response is handled independently. Hence, a HTTP server cannot know whether a series of request sent from the same client or from different clients. That means the server cannot maintain client's state between multiple requests.
For HTTP protocol's characteristic, application servers maintain a session to keep client's state. When the server receives the first request from a client, the server creates a session and give the session a unique identifier. The client should send a request with the session identifier. The server can determine which session the request belongs to upon this session identifier.
In Java EE environment, an application server creates a javax.servlet.http.HttpSession object to track client's session. ZK's Session is a wrapper of HttpSession, you can use it to store user's data when you handle events. The usage:
- Get current session: Sessions.getCurrent()
- Store data into a session: Session.setAttribute("key", data)
- Retrieve data from a session: Session.getAttribute("key")
Login
After understanding basic concept and usage, we can start to create a controller for login. This controller collects account and password input and validate them with a authentication service class. If the password is correct, the authentication service class saves user's credential into the session.
Controller used in chapter8/login.zul
public class LoginController extends SelectorComposer<Component> {
//wire components
@Wire
Textbox account;
@Wire
Textbox password;
@Wire
Label message;
//services
AuthenticationService authService = new AuthenticationServiceChapter8Impl();
@Listen("onClick=#login; onOK=#loginWin")
public void doLogin(){
String nm = account.getValue();
String pd = password.getValue();
if(!authService.login(nm,pd)){
message.setValue("account or password are not correct.");
return;
}
UserCredential cre= authService.getUserCredential();
message.setValue("Welcome, "+cre.getName());
message.setSclass("");
Executions.sendRedirect("/chapter8/");
}
}
- Line 20: Authenticate a user with account and password and save user's credential into the session if it passes.
- Line 28: Redirect to index page after successfully authenticated.
Authentication service class used in LoginController
public class AuthenticationServiceChapter8Impl extends AuthenticationServiceChapter5Impl{
UserInfoService userInfoService = new UserInfoServiceChapter5Impl();
@Override
public boolean login(String nm, String pd) {
User user = userInfoService.findUser(nm);
//a simple plan text password verification
if(user==null || !user.getPassword().equals(pd)){
return false;
}
Session sess = Sessions.getCurrent();
UserCredential cre = new UserCredential(user.getAccount(),user.getFullName());
//just in case for this demo.
if(cre.isAnonymous()){
return false;
}
sess.setAttribute("userCredential",cre);
return true;
}
...
}
- Line 14: Get the current session.
- Line 20: Store user credential into the session with a key userCredential which is used to retrieve credential back in the future.
After login, we want to display user's account in the banner. We can use EL to get user's account from UserCredential in the session.
<div hflex="1" vflex="1" sclass="banner">
<hbox hflex="1" vflex="1" align="center">
<!-- other components -->
<hbox apply="org.zkoss.tutorial.chapter8.LogoutController"
hflex="1" vflex="1" pack="end" align="end" >
<label value="${sessionScope.userCredential.name}" if="${not sessionScope.userCredential.anonymous}"/>
<label id="logout" value="Logout" if="${not sessionScope.userCredential.anonymous}" sclass="logout"/>
</hbox>
</hbox>
</div>
- Line 7: The sessionScope is an implicit object that you can use within EL to access session's attribute. It works as the same as getAttribute(). You can use it to get session's attribute with dot notation, e.g. ${sessionScope.userCredential} equals to calling getAttribute("userCredential") of a Session object.
Logout
When a user logout, we usually clear his data in the session and redirect him to login page. In our example, click "Logout" label in the banner can log you out.
<div hflex="1" vflex="1" sclass="banner" high='7'>
<hbox hflex="1" vflex="1" align="center">
<!-- other components -->
<hbox apply="org.zkoss.tutorial.chapter8.LogoutController"
hflex="1" vflex="1" pack="end" align="end" >
<label value="${sessionScope.userCredential.name}" if="${not sessionScope.userCredential.anonymous}"/>
<label id="logout" value="Logout" if="${not sessionScope.userCredential.anonymous}" sclass="logout"/>
</hbox>
</hbox>
</div>
Secure Your Pages
Security is an important issue that every developer must consider for a web application since users can enter any URL in a browser to access any resource. Even though you have created a login page for authentication, but a user can bypass the login page and access the index page after login if he knows the page's URL. Therefore, we should apply a mechanism to protect zul pages from illegal access. ZK allows you to initialize a zul page by implement a Initiator. When we apply an Initiator to a zul, ZK will use it to perform initialization before creating components upon a zul. We can create a initiator to check user's