Book not found

From Documentation
⧼coll-notfound_msg⧽

Return to Documentation.

DocumentationZK EssentialsChapter 8: Authentication
Chapter 8: Authentication



WarningTriangle-32x32.png This page is under construction, so we cannot guarantee the accuracy of the content!



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:

Tutorial-ch8-login.png


After login, we redirect users to index page with user name appeared at right side of the header.

Tutorial-ch8-index.png



Authentication & Session

Before proceeding to implement a 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 a 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")


Almost all business applications require a security mechanism and authentication is the fundamental requirement of it. Some resources are only available to those authenticated users. Authentication is the process to verify that a user is who he claims to be. After we authenticate a user, the application needs to remember the user and identifies his subsequent requests so that the application doesn't need to authenticate him repeatedly for each further request. Hence, storing a user credential in a session is a good practice and we can tell whom the request belongs to by the reques't session. Besides, we can't tell whether a request comes from an authenticated user by checking user's credential in the request's session.


Secure Your Pages

Even you have setuped a authentication mechanism, but a user still can access a page if he knows the page's URL. Therefore, we should protect a page from illegal access by checking user's credentials in his session when a page is requested by a user.

Authentication Service

We have implemented a service class that get a user's credentials from his session.

public class AuthenticationServiceChapter5Impl implements AuthenticationService,Serializable{
	

	public UserCredential getUserCredential(){
		Session sess = Sessions.getCurrent();
		UserCredential cre = (UserCredential)sess.getAttribute("userCredential");
		if(cre==null){
			cre = new UserCredential();//new a anonymous user and set to session
			sess.setAttribute("userCredential",cre);
		}
		return cre;
	}
...
}
  • Line 5, 6: As we mentioned, we can get current session and check user's credentials to verify its authentication status.

Page Initialization

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 the zul.

We can create an initiator to check existence of a user's credentials in the session. If a user's credentials is absent, we determine it's a illegal request and redirect it back to login page.

Page initiator to check a user's credentials

public class AuthenticationInit implements Initiator {

	//services
	AuthenticationService authService = new AuthenticationServiceChapter8Impl();
	
	public void doInit(Page page, Map<String, Object> args) throws Exception {
		
		UserCredential cre = authService.getUserCredential();
		if(cre==null || cre.isAnonymous()){
			Executions.sendRedirect("/chapter8/login.zul");
			return;
		}
	}
}
  • Line 1, 6: A page initiator class should implement Initiator and override doInit().
  • Line 8: Get a user's credentials from current session.
  • Line 10: Redirect users back to login page.


Then we can apply this page initiator to those pages we want to protect from unauthenticated access.

chapter8/index.zul

<?link rel="stylesheet" type="text/css" href="/style.css"?>
<!-- protect page by the authentication init  -->
<?init class="org.zkoss.tutorial.chapter8.AuthenticationInit"?>
<!-- authentication init have to locate before composition -->
<?init class="org.zkoss.zk.ui.util.Composition" arg0="/chapter8/layout/template.zul"?>

<zk>
	<include id="mainInclude" self="@define(content)" src="/chapter8/home.zul"/>
</zk>
  • Line 3: Because index.zul is the main page, we apply this page initiator on it.

After above steps, if you directly visit http://localhost:8080/tutorial/chapter8/index.zul without success authentication, you still see the login page.


The "if" Attribute

We can use EL expression in if attribute to show a component according to a user's credentials in the session. If the evaluation result of the EL expression is true, the component will show otherwise the component will not be created.

chapter8/layout/template.zul

<zk>
	<!-- create only when the currentUser is not an anonymous  -->
	<borderlayout hflex="1" vflex="1" if="${not sessionScope.userCredential.anonymous}">
		...
	</borderlayout>
	<div if="${sessionScope.userCredential.anonymous}">
		Redirect to login page.....
	</div>
</zk>
  • Line 3,6: sessionScope is an implicit variable that allows you to access session's attributes. The Borderlayout is only created when userCredential is not anonymous, otherwise it shows the Div for redirect.

Login

It is a common way to request an account and a password for authentication. We create a login page to collect user's account and password and the 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. As this page is made for anonymous users, we don't have to protect this page.


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="&#160;" />
					<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.


This login 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's login() 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.

chpater8/layout/banner.zul

<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 credentials in the session and redirect him to login page. In our example, click "Logout" label in the banner can log you out.

chpater8/layout/banner.zul

<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 8: We listen onClick event on logout label to perform logout action.

LogoutController.java

public class LogoutController extends SelectorComposer<Component> {
	
	//services
	AuthenticationService authService = new AuthenticationServiceChapter8Impl();
	
	@Listen("onClick=#logout")
	public void doLogout(){
		authService.logout();		
		Executions.sendRedirect("/chapter8/");
	}
}
  • Line 8: Call service class to perform logout.
  • Line 9: Redirect users to login page.


Authentication service's logout() used in LogoutController

public class AuthenticationServiceChapter8Impl extends AuthenticationServiceChapter5Impl{

	
	UserInfoService userInfoService = new UserInfoServiceChapter5Impl();
	
	...
	
	@Override
	public void logout() {
		Session sess = Sessions.getCurrent();
		sess.removeAttribute("userCredential");
	}
}
  • Line 11: Remove the stored user credential in the session.




Last Update : 2013/02/22

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