You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cocoon.apache.org by Ulrich Mayring <ul...@denic.de> on 2000/07/13 15:21:34 UTC

Session/authentication scheme (long, code)

Some users have requested to see how I did sessions and authentication
against a database. So I am posting it to the list. To be sure, it's
just a quick'n'dirty thing, but it sort of works (see DISCLAIMER).

<DISCLAIMER>
It works with JSDK2.0 (which JServ wants), but it uses Java APIs that
are deprecated with JSDK2.2 (which Tomcat wants). It is easy to change
the deprecated methods, because there are equivalent replacements in
JSDK2.2 - but it is not so easy to write a version that works with
JSDK2.0 and JSDK2.2 at the same time. However, I have not looked very
hard, it may be possible.
</DISCLAIMER>

Suppose I have a directory, which contains some sensible files, that
should only be accessible after authentication. Each of these files
should look like this:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<?xml-stylesheet href="mystyle.xsl" type="text/xsl"?>

<?cocoon-process type="xinclude"?>
<?cocoon-process type="xsp"?>
<?cocoon-process type="xslt"?>
<? ... more PIs, depending on what your page does ... ?>

<page>

<xinclude:include parse="xml" href="session.xsp#xpointer(//include)"/>

<title>The Title of This Page</title>

... content ... whatever this page does ...

</page>

So, everything from session.xsp within the <include> tag is always
included. Here is what session.xsp looks like:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>

<xsp:page language="java"
	xmlns:xsp="http://www.apache.org/1999/XSP/Core"
	xmlns:response="http://www.apache.org/1999/XSP/Response">

<include>

<xsp:logic>
	if (session != null) {
		<connectiondefs>
			<server><xsp:expr>session.getValue("server")</xsp:expr></server>
			<connection name="foo_connection">
				<driver><xsp:expr>session.getValue("driver")</xsp:expr></driver>
			
<username><xsp:expr>session.getValue("username")</xsp:expr></username>
			
<password><xsp:expr>session.getValue("password")</xsp:expr></password>
				<dburl><xsp:expr>session.getValue("dburl")</xsp:expr></dburl>
			</connection>
		</connectiondefs>
		}
	else {
		<response:send-redirect location="login.xml"/>
		}
</xsp:logic>

</include>

</xsp:page>

As you can see, the necessary parameters for connecting to a database
(or doing something else) are pulled out of the session object - if
there is no session object, then the user is automatically redirected to
login.xml. This means that if the user is not logged in, but tries to
access a sensitive page by typing in its URL, then he won't see
anything, but is redirected to login.xml, which looks like this:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<?xml-stylesheet href="login.xsl" type="text/xsl"?>

<?cocoon-process type="xsp"?>
<?cocoon-process type="xslt"?>

<xsp:page language="java"
	xmlns:xsp="http://www.apache.org/1999/XSP/Core">

<page>

<title>User Authentication</title>

<xsp:logic>
	if (session != null) {
		<message>
			<xsp:expr>"You are logged in as user " + session.getValue("username")
+
			" on server " + session.getValue("server") + "."</xsp:expr>
		</message>
		}
	else {
		String message = request.getParameter("message");
		if (message != null) {
			<message><xsp:expr>message</xsp:expr></message>
			}
		else {
			<message>You are not authenticated.</message>
		}
</xsp:logic>

<formset url="session.xml" button1="Login" button2="Logout">
	<formselect>
		<name>Server</name>
		<param>server</param>
		<entry>server1.company.com</entry>
		<entry>server2.company.com</entry>
		<entry>server3.company.com</entry>
	</formselect>
	<formselect>
		<name>Username</name>
		<param>username</param>
		<entry>user1</entry>
		<entry>user2</entry>
		<entry>user3</entry>
	</formselect>
	<forminputfield>
		<name>Password</name>
		<param>password</param>
	</forminputfield>
</formset>

</page>

</xsp:page>

This creates a form with two popup-menus for server and username and a
text entry field for password. Here are the excerpts of login.xsl, which
create this HTML form:

<xsl:variable name="blue">#039acc</xsl:variable>

<xsl:template match="title">
	<h1><xsl:apply-templates/></h1>
</xsl:template>

<xsl:template match="formset">
	<form action="{@url}" method="POST">
		<table border="0" cellpadding="0" cellspacing="3">
			<xsl:apply-templates/>
			<tr><td colspan="2" align="right">
				<input type="submit" name="submit" value="{@button1}"/>&#160;
				<input type="submit" name="invalidate" value="{@button2}"/>&#160;
			</td></tr>
		</table>
	</form>
</xsl:template>

<xsl:template match="forminputfield">
	<tr>
		<td align="right" valign="middle" bgcolor="{$blue}" width="25%">
			<b>&#160;<xsl:value-of select="name"/>:</b>&#160;
		&#160;</td>
		<td align="left"
			<input type="password" name="{param}" size="30" value=""/>&#160;
		</td>
	</tr>
</xsl:template>

<xsl:template match="formselect">
	<tr>
		<td align="right" valign="middle" bgcolor="{$blue}" width="25%">
			<b>&#160;<xsl:value-of select="name"/>:</b>&#160;
		&#160;</td>
		<td align="left">
			<select name="{param}" size="1">
				<xsl:for-each select="entry">
					<option value="{.}"><xsl:value-of select="."/></option>
				</xsl:for-each>
			</select>
		</td>
	</tr>
</xsl:template>

<xsl:template match="message">
	<p><b>
		<font color="{$blue}">
			<xsl:apply-templates/>
		</font>
	</b></p>
</xsl:template>

So, once you have this form the user can select a server and a username,
he can type in a password and then he can either press "Login" or
"Logout". The form is posted to session.xml, which does authentication
and session creation. It looks like this:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>

<?cocoon-process type="xsp"?>

<xsp:page language="java"
	xmlns:xsp="http://www.apache.org/1999/XSP/Core"
	xmlns:response="http://www.apache.org/1999/XSP/Response">

<xsp:structure>
	<xsp:include>java.sql.*</xsp:include><!-- the JDBC API -->
</xsp:structure>

<page>

<xsp:logic>
	if (request.getParameter("invalidate") != null) {        /* user
pressed logout button */
		if (session != null) session.invalidate();       /* kill the session,
unless there is none */
		<response:send-redirect location="login.xml"/>   /* back to login page
*/
		}
	else {
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		String server = request.getParameter("server");
		String dburl = "jdbc:path:to:my:db:" + server + ":5555";  /* edit this
to reflect your JDBC URL */
		String driver = "my.driver.class.Name";                   /* edit this
to reflect your driver name */

		try {
			Class.forName(driver);
			Connection con =
DriverManager.getConnection(dburl,username,password);
			con.close();
			/* If we get this far without throwing an exception, then we have
successfully
			   logged into and out of the database. Further authentication tests
could be performed,
			   for example if you want to know which user logged in and grant him
certain rights.
			   For me it is enough to know the user can log in, i.e. the database
knows him. */
			session = request.getSession(true);
			session.putValue("username",username);
			session.putValue("password",password);
			session.putValue("dburl",dburl);
			session.putValue("driver",driver);
			session.putValue("server",server);
			<response:send-redirect location="index.xml"/>
			/* index.xml is the start page the user should see after successful
authentication */
			}
		catch (Exception e) {
			<response:send-redirect
location="login.xml?message=Authentication%20failed."/>
			/* Send user back to login.xml and print message that authentication
failed.
			   More meaningful error messages could be printed, if I would not
lump all exceptions
			   into one, but were to differentiate between various types of
exceptions */
			}
		}
	</xsp:logic>

</page>

</xsp:page>

Finally, to polish things up, here's an excerpt from mystyle.xsl, the
stylesheet that is used by all the sensitive pages except the login page
(which uses login.xsl from above):

<xsl:template match="title">
	<table border="0" cellpadding="0" cellspacing="0">	
		<tr>
			<td>
				<h1><xsl:apply-templates/>&#160;&#160;&#160;</h1>
			</td>
			<td>
				<form action="session.xml" method="POST">
					<input type="submit" name="invalidate" value="Logout"/>
				</form>
			</td>
		</tr>
	</table>
</xsl:template>

This template simply prints the page's <title> as an HTML headline and
puts a logout button next to it. This is for convenience, imagine that
your users have several roles: a user might be on a page and decide he
needs to log in as another role, in order to have more rights. Since
every page, that has a <title>, now also has a logout button, the user
can immediately press it, the session is invalidated and he is taken
back to login.xml, where he can log in as another user. Or he can go
home :-)

As you can see this implementation is not very nice as far as
encapsulation goes, so I really should make a taglib out of it. My code
does some amount of form handling, this seems to be the norm nowadays
for code posted to this list. So we really should have a form handling
taglib as well.

Ulrich

-- 
Ulrich Mayring
DENIC eG, Systementwicklung