How to customize a spring-security form-login. Part 1

An article from my zk integration series.

part 2
part 3
part 4
part 5

For a spring-security 3.0.2 managed project i need a third parameter in the login dialog. This third parameter should represent a tenant id for a multi-tenant application. The logic should lookup in the tenant administrations database for the used table schema name in which the tenants data are stored. Let me early say that the best practice not only for multi-tenant database applications is the way to separate completely the user/tenant and right data from the applications data. This separation is a basic necessity for high scaling applications. In doing so it’s equal if the users data are stored in a LDAP or relational database server. Mostly an LDAP server is prefered because it’s optimized for fast reading, means 1000’s of user entries in a second.

Here’s an picture on how the login dialog should looks like:

ZKLoginDialog

customized spring-security form-login dialog with zk framework

zkloginDialog.zul

<?page id="ZKLoginDialog" title="LOGIN"?>
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>

<zk xmlns="http://www.zkoss.org/2005/zul"
	xmlns:h="http://www.w3.org/1999/xhtml"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.zkoss.org/2005/zul http://www.zkoss.org/2005/zul/zul.xsd">

	<style>
		body { padding: 0 0; /* 0 padding on top and bottom and 0
		padding on right and left */

		<!-- background image -->
		background-image:
		url(${c:encodeURL('/images/templates/test/Grey_T_R.jpg')});
		background-repeat:repeat-x; }

		<!-- cut the vertical borders in the rows -->
		tr.z-row td.z-row-inner { border-right: 0px #CCC; }

		<!-- nicer looking -->
		.z-window-modal-cnt-noborder {background: none}
		.z-window-modal-cl-noborder {background: none}

		.outerGroupBox .z-groupbox-cnt {padding: 0px;}

		<!-- Make Plain Grid -->
		.GridLayoutNoBorder tr.z-row td.z-row-inner, tr.z-row
		.z-cell,div.z-grid { border: none; overflow: hidden; zoom: 1;
		background: white; border-top: none; border-left: none;
		border-right: none; border-bottom: none; }
	</style>

	<window id="loginwin" border="none" width="390px"
		apply="de.forsthaus.webui.login.ZkLoginDialogCtrl">

		<groupbox mold="3d" closable="false" sclass="outerGroupBox"
			contentStyle="background-color : white">
			<caption label="Login" image="/images/earth2.gif"
				style="font-weight: bold;">

				<button id="button_ZKLoginDialog_Close"
					image="/images/icons/stop.gif"
					tooltiptext="${c:l('button_ZKLoginDialog_Close.tooltiptext')}" />
			</caption>

			<columnlayout
				style="background-image:url(${c:encodeURL('/images/login_151x222.jpg')}); ">
				<columnchildren width="30%" />

				<columnchildren width="70%">
					<panel border="none">
						<panelchildren>
							<separator />
							<groupbox
								if="${not empty param.login_error}">
								<label style="color:red"
									value="Login failed. Please try again." />
								<h:br />
								<label style="color:red"
									value="Reason: ${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}" />
								<h:br />
							</groupbox>

							<groupbox
								style="padding-top: 5px; padding-left: 5px; ">
								<caption label="Login" />

								<div>
									<!-- ### Spring Security action-url = j_spring_security_check  ### -->
									<h:form id="f" name="f"
										action="j_spring_security_check" method="POST">

										<grid fixedLayout="true"
											sclass="GridLayoutNoBorder" style="border:0px">
											<columns>
												<column width="40%" />
												<column width="60%" />
											</columns>
											<rows>

												<!-- TenantID -->
												<row>
													<label
														id="label_ZKLoginDialog_tenant"
														value="${c:l('label_ZKLoginDialogTenant')}" />
													<textbox
														id="txtb_TenantId" name="j_tenantid" width="98%" />
												</row>

												<!-- Username -->
												<row>
													<label
														id="label_ZKLoginDialog_user"
														value="${c:l('label_ZKLoginDialogUser')}" />
													<textbox
														id="txtb_Username" name="j_username" width="98%" />
												</row>

												<!-- Password -->
												<row>
													<label
														id="label_ZKLoginDialog_pwd"
														value="${c:l('label_ZKLoginDialogPwd')}" />
													<textbox
														id="txtb_Password" type="password" name="j_password"
														width="98%" />
												</row>

												<row spans="2">
													<hbox>
														<h:input
															type="submit" value="Login" />
														<!-- 
															<button id="btnReset" 
															height="22px" label="Reset" />
														-->
													</hbox>
												</row>
											</rows>
										</grid>
									</h:form>
								</div>

							</groupbox>

						</panelchildren>
					</panel>
				</columnchildren>
			</columnlayout>

		</groupbox>

	</window>
</zk>

The interesting part of this file are the three variable names: j_tenantid, j_username, j_password . These are submitting to the server when pressing the OK button .

  • ZKLoginDialogCtrl.java

    /**
     * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<br>
     * Controller for the /WEB-INF/zkloginDialog.zul file.<br>
     * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<br>
     * 
     * @author bbruhns
     * @author sgerth
     */
    public class ZkLoginDialogCtrl extends GenericForwardComposer implements Serializable {
    
    	private transient final static Logger logger = Logger.getLogger(ZkLoginDialogCtrl.class);
    	private static final long serialVersionUID = -71422545405325060L;
    
    	/*
    	 * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    	 * All the components that are defined here and have a corresponding
    	 * component with the same 'id' in the zul-file are getting autowired 
         * by the 'GenericForwardComposer'.
    	 * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    	 */
    	protected Window loginwin; // autowired
    	protected Textbox txtb_Username; // autowired
    	protected Textbox txtb_Password; // autowired
    	protected Textbox txtb_TenantId; // autowired
    
    	/**
    	 * default constructor. <br>
    	 */
    	public ZkLoginDialogCtrl() {
    		super();
    
    		if (logger.isDebugEnabled()) {
    			logger.debug("--> super() ");
    		}
    	}
    
    	/**
    	 * Automatically called method from zk.
    	 * 
    	 * @param event
    	 * @throws Exception
    	 */
    	public void onCreate$loginwin(Event event) throws Exception {
    
    		if (logger.isDebugEnabled()) {
    			logger.debug("--> " + event.toString());
    		}
    
    		txtb_Username.focus(); // set the focus on UserName
    
    		loginwin.setShadow(false);
    		loginwin.doModal();
    	}
    
    	/**
    	 * when the "close" button is clicked. <br>
    	 * 
    	 * @throws IOException
    	 */
    	public void onClick$button_ZKLoginDialog_Close() throws IOException {
    
    		if (logger.isDebugEnabled()) {
    			logger.debug("-->");
    		}
    
    		Executions.sendRedirect("/j_spring_logout");
    	}
    
    }
    

    Ok. Back to the customizing. In this article i will not explain how spring-security works therefore’s the documentation. I will concentrate on the classes for the needed purpose. At first we have a look which classes are involved in the case of spring-security’s form-login.

    This we will done in part 2 of this article.

    Samples are hostet in the Zksample2 project on

    Have fun with it.

    Stephan Gerth

    Dipl.rer.pol.


    PS: Help to prevent the global warming by writing cool software

    Leave a Reply

    You must be logged in to post a comment.