src.com.ibm.as400.util.servlet.AuthenticationServlet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jt400-jdk8 Show documentation
Show all versions of jt400-jdk8 Show documentation
The Open Source version of the IBM Toolbox for Java
///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename: AuthenticationServlet.java
//
// The source code contained herein is licensed under the IBM Public License
// Version 1.0, which has been approved by the Open Source Initiative.
// Copyright (C) 1997-2000 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
package com.ibm.as400.util.servlet;
import java.util.Hashtable;
import java.util.Properties;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import com.ibm.as400.access.Log; //$A2C
import com.ibm.as400.access.Trace;
import com.ibm.as400.util.BASE64Decoder;
/**
* AuthenticationServlet is an HttpServlet implementation that performs basic authentication for servlets.
* Subclasses should override the validateAuthority() method to perform the authentication. The bypassValidation()
* method can be overridden to authenticate only certain requests and the postValidation() method can be overridden
* for additional processing of the request after authenticating.
**/
public class AuthenticationServlet extends HttpServlet
{
static final long serialVersionUID = 3761917964251765027L;
/**
* The realm used for this servlet instance.
**/
private String realm_ = "localhost";
/**
* The actual name the user will see. This may be the same as realm in some cases. A realm of
* "localhost" may be meaningless to the user, while a realmDisplayName of "mysystem" may be more
* meaningful.
**/
private String realmDisplayName_;
/**
* Hashtable used to keep session data.
**/
private Hashtable sessionTable_ = new Hashtable();
/**
* The log to use for logging traces and errors. //$A2C
**/
private Log log_; //$A2C
// Handles loading the appropriate resource bundle
private static ResourceBundleLoader_s loader_; //$A1A
/**
* Constructs a default AuthenticationServlet object.
**/
public AuthenticationServlet()
{
super();
}
/**
* Constructs an AuthenticationServlet object with the specified user, password, and realm.
*
* @param user The user ID to use.
* @param password The password for this user ID.
* @param realm The realm, which refers to the system name.
**/
public AuthenticationServlet(String user, String password, String realm)
{
super();
setUser(user);
setPassword(password);
setRealm(realm);
}
/**
* Method to check to see if authentication should be performed. The default implementation returns false.
* Subclasses that wish to implement authentication based on the URL can override this method, interrogate
* the request object and determine if authentication should be performed.
*
* @param req The HttpServletRequest object for this request.
* @return true if authentication should not be performed.
*/
public boolean bypassAuthentication(HttpServletRequest req)
{
return false;
}
/**
* Get the log object used for tracing and error logging.
*
* @return The Log object to use for this servlet.
*/
public Log getLog()
{
return log_;
}
/**
* Retrieve the user that was used for the authentication.
*
* @return The authenticated user ID.
*/
public String getUser()
{
Thread currentThread = Thread.currentThread();
String threadId = currentThread.getName();
Properties p = (Properties)sessionTable_.get(threadId);
return p.getProperty("uid");
}
/**
* Retrieve the realm that was used for the authentication. For the IBM i system, the realm is the
* system name.
*
* @return The realm.
*/
public String getRealm()
{
return realm_;
}
/**
* Retrieve the Hashtable used to keep session data
*
* @return The session data.
*/
Hashtable getSessionData()
{
return sessionTable_;
}
/**
* Servlet initialization. The realm is initialized at this point to localhost. It can be overridden
* by the setRealm() method.
*
* @param config The servlet configuration.
* @exception ServletException A ServletException is thrown if a problem with the servlet occurs.
*/
public void init(ServletConfig config)
throws ServletException
{
log_ = new ServletEventLog(config);
super.init(config);
setRealm("localhost"); // @A7C
}
/**
* Log a message to the event log.
*
* @param msg The message to log.
*/
public void log(String msg)
{
if (log_ != null)
log_.log(msg);
}
/**
* Log an exception and message to the event log.
*
* @param e The exception to log.
* @param msg The message to log.
*/
public void log(Exception e, String msg)
{
if (log_ != null)
log_.log(msg, e);
}
/**
* Send the authentication response to the client.
*
* @param resp The HttpServletResponse object to use.
* @param realmDisplayName The realm name to be displayed by the client.
* @exception IOException An IOException is thrown if a communications error occurs.
*/
private void sendAuthenticationResponse(HttpServletResponse resp, String realmDisplayName)
throws IOException
{
log(loader_.substitute(loader_.getText("PROP_DESC_CHALLENGE"), new String[] {realmDisplayName_})); //$A1C
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
resp.setHeader("Www-authenticate", "Basic realm=\"" + realmDisplayName_ + "\"");
resp.setContentType("text/html");
}
/**
* Set the log object used for tracing and error logging.
*
* @param log The Log.
*/
public void setLog(Log log) //$A2C
{
if (log == null)
throw new NullPointerException("log");
log_ = log; //$A2C
}
/**
* Set the password. This method can be used to set the password to a default password after bypassing
* authentication.
*
* @param password The password to use.
*/
public void setPassword(String password)
{
if (password == null)
throw new NullPointerException("password");
Thread currentThread = Thread.currentThread();
String threadId = currentThread.getName();
Properties p = (Properties)sessionTable_.get(threadId);
if (p == null)
p = new Properties();
p.put("pw", password);
}
/**
* Override the default service() method for HttpServlet. Subclasses should not override this method unless
* necessary. If a subclass overrides this method, it should call super.service() or authentication would
* not occur for the servlet.
*
* @param req The HTTP servlet request.
* @param resp The HTTP servlet response.
* @exception ServletException A ServletException is thrown if a problem with the servlet occurs.
* @exception IOException An IOException is thrown if a communications error occurs.
**/
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
log(loader_.substitute(loader_.getText("PROP_DESC_SERVICE"), new String[] {req.getRemoteHost(), req.getRemoteAddr()})); //$A1C
// check to see if we should authenticate. Call bypassAuthentication() method, which can be overridden
// by subclasses to see if authentication should be performed. Default implementation is to authenticate
// all requests. Subclasses can override the method and interrogate the request object to determine
// if authentication is needed.
if (!bypassAuthentication(req))
{
// see if the header contained realm user ID and password
String uidpw = req.getHeader("Authorization");
if (uidpw != null)
{
int index = uidpw.indexOf(" ");
uidpw = uidpw.substring(index+1);
// now decode from base64
BASE64Decoder decoder = new BASE64Decoder();
byte[] buffer = decoder.decodeBuffer(uidpw);
String s = new String(buffer);
index = s.indexOf(":");
String uid, pw; // $A8C
// If a character causes the ':' and password which follows to be omitted from the // $A8A
// authoriztion header, then we need to ensure the password is set to an empty // $A8A
// string, which will allow the validateAuthority method to gracefully handle // $A8A
// an invalid uid/pwd string instead of generating exceptions. // $A8A
if (index == -1) // $A8A
{
uid = s; // $A8C
pw = ""; // $A8C
Trace.log(Trace.INFORMATION, "Missing ':' (colon) in authorization header."); // $A8A
} // $A8A
else // $A8A
{
uid = s.substring(0, index);
pw = s.substring(index+1);
}
try
{
log(loader_.substitute(loader_.getText("PROP_DESC_AUTHENTICATING"), new String[] {realm_, uid})); //$A1C
// $A5A
// The return from validateAuthority will tell if the caller wants to continue validation.
// When the caller overrides validateAuthority() they can then return false, which will
// allow them to display there own HTML error message/page in the browser instead of
// getting the default browser message (Error: 503... etc) when an exception occurs.
if (!validateAuthority(realm_, uid, pw)) //$A5C
return; //$A5A
Thread currentThread = Thread.currentThread();
String threadId = currentThread.getName();
Properties p = new Properties();
p.put("realm", realm_);
p.put("uid", uid);
p.put("pwd", pw);
sessionTable_.put(threadId, p);
log( loader_.substitute( loader_.getText( "PROP_DESC_AUTHENTICATED"), new String[ ] {realm_, uid})); //$A1C
//$A5A
// The return from postValidation will tell if the caller wants to continue validation.
// When the caller overrides postValidation() they can then return false, which will
// allow them to display there own HTML error message/page in the browser instead of
// getting the default browser message (Error: 503... etc) when an exception occurs.
if (!postValidation(req, resp)) //$A5C
return; //$A5A
super.service(req, resp);
sessionTable_.remove(threadId);
}
catch (SecurityException se)
{
log(loader_.substitute(loader_.getText("PROP_DESC_AUTHENTICATEFAILED"), new String[] {uid, se.getMessage()})); //$A1C $A6C
if (Trace.isTraceOn()) //$A6A
Trace.log(Trace.ERROR, se); //$A6A
sendAuthenticationResponse(resp, realmDisplayName_);
}
catch (Exception e)
{
log(loader_.substitute(loader_.getText("PROP_DESC_REQFAILED"), new String[] {uid, e.getMessage()})); //$A1C $A6C
if (Trace.isTraceOn()) //$A6A
Trace.log(Trace.ERROR, e); //$A6A
if (e instanceof IOException) //$A4A
throw (IOException)e; //$A4A
else if (e instanceof ServletException) //$A4A
throw (ServletException)e; //$A4A
else if (e instanceof RuntimeException) // @A4C
throw (RuntimeException)e; // @A4C
else //$A4C
throw new ServletException(e.getMessage()); // @A4C
}
log(loader_.substitute(loader_.getText("PROP_DESC_REQCOMPLETED"), new String[] {req.getRemoteHost(), req.getRemoteAddr()})); //$A1C
return;
}
// respond with realm challenge
sendAuthenticationResponse(resp, realmDisplayName_);
}
else
{
super.service(req, resp);
}
}
/**
* Set the realm that will be used for the authentication. For the IBM i system, the realm is the
* system name.
*
* @param realm The realm, which refers to the system name.
**/
public void setRealm(String realm)
{
if (realm == null)
throw new NullPointerException("realm");
if (realm.equalsIgnoreCase("localhost"))
{
try
{
InetAddress local = InetAddress.getLocalHost();
realmDisplayName_ = local.getHostName();
realm_ = realmDisplayName_; // @A7A
}
catch (UnknownHostException e)
{
log(loader_.getText("PROP_DESC_REALMFAILED")); //$A1C $A6C
if (Trace.isTraceOn()) //$A6A
Trace.log(Trace.ERROR, e); //$A6A
realm_ = realm; // @A7A
realmDisplayName_ = realm;
}
}
else
{
realm_ = realm; // @A7A
realmDisplayName_ = realm;
}
}
/**
* Set the user ID. This method can be used to set the user ID to a default user after bypassing
* authenticaiton.
*
* @param user The user ID to use.
**/
public void setUser(String user)
{
if (user == null)
throw new NullPointerException("user");
Thread currentThread = Thread.currentThread();
String threadId = currentThread.getName();
Properties p = (Properties)sessionTable_.get(threadId);
if (p == null)
p = new Properties();
p.put("uid", user);
}
/**
* Method used to validate. The default implementation does nothing. Subclasses should override this method
* and implement appropriate validation scheme.
*
* @param realm The realm to validate against.
* @param uid The user ID to use for validation.
* @param pw The password to use for validation.
*
* @return true if the servlet should continue authenticating; false otherwise. The default is true;
*
* @exception SecurityException This exception should be thrown if validation fails.
* @exception IOException This exception should be thrown if a communication error occurs during validation.
**/
public boolean validateAuthority(String realm, String uid, String pw) //$A5C
throws SecurityException, IOException
{
return true;
}
/**
* Method called after validation has occured. The default implementation does nothing.
* Subclasses should override this method to continue processing the request.
*
* @param req The HTTP servlet request.
* @param resp The HTTP servlet response.
*
* @return true if the servlet should continue authenticating; false otherwise. The default is true;
*
* @exception ServletException A ServletException is thrown if a problem with the servlet occurs.
* @exception IOException An IOException is thrown if a communications error occurs.
**/
public boolean postValidation(HttpServletRequest req, HttpServletResponse resp) //$A3 //$A5C
throws ServletException, IOException
{
return true;
}
}