DigestAuthenticatorpublic class DigestAuthenticator extends AuthenticatorBase An Authenticator and Valve implementation of HTTP DIGEST
Authentication (see RFC 2069). |
Fields Summary |
---|
private static org.apache.juli.logging.Log | log | protected static final org.apache.catalina.util.MD5Encoder | md5EncoderThe MD5 helper object for this class. | protected static final String | infoDescriptive information about this implementation. | protected static MessageDigest | md5HelperMD5 message digest provider. | protected String | keyPrivate key. |
Constructors Summary |
---|
public DigestAuthenticator()
// ----------------------------------------------------------- Constructors
super();
try {
if (md5Helper == null)
md5Helper = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new IllegalStateException();
}
|
Methods Summary |
---|
public boolean | authenticate(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response, org.apache.catalina.deploy.LoginConfig config)Authenticate the user making this request, based on the specified
login configuration. Return true if any specified
constraint has been satisfied, or false if we have
created a response challenge already.
// Have we already authenticated someone?
Principal principal = request.getUserPrincipal();
//String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
if (principal != null) {
if (log.isDebugEnabled())
log.debug("Already authenticated '" + principal.getName() + "'");
// Associate the session with any existing SSO session in order
// to get coordinated session invalidation at logout
String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
if (ssoId != null)
associate(ssoId, request.getSessionInternal(true));
return (true);
}
// NOTE: We don't try to reauthenticate using any existing SSO session,
// because that will only work if the original authentication was
// BASIC or FORM, which are less secure than the DIGEST auth-type
// specified for this webapp
//
// Uncomment below to allow previous FORM or BASIC authentications
// to authenticate users for this webapp
// TODO make this a configurable attribute (in SingleSignOn??)
/*
// Is there an SSO session against which we can try to reauthenticate?
if (ssoId != null) {
if (log.isDebugEnabled())
log.debug("SSO Id " + ssoId + " set; attempting " +
"reauthentication");
// Try to reauthenticate using data cached by SSO. If this fails,
// either the original SSO logon was of DIGEST or SSL (which
// we can't reauthenticate ourselves because there is no
// cached username and password), or the realm denied
// the user's reauthentication for some reason.
// In either case we have to prompt the user for a logon
if (reauthenticateFromSSO(ssoId, request))
return true;
}
*/
// Validate any credentials already included with this request
String authorization = request.getHeader("authorization");
if (authorization != null) {
principal = findPrincipal(request, authorization, context.getRealm());
if (principal != null) {
String username = parseUsername(authorization);
register(request, response, principal,
Constants.DIGEST_METHOD,
username, null);
return (true);
}
}
// Send an "unauthorized" response and an appropriate challenge
// Next, generate a nOnce token (that is a token which is supposed
// to be unique).
String nOnce = generateNOnce(request);
setAuthenticateHeader(request, response, config, nOnce);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
// hres.flushBuffer();
return (false);
| protected static java.security.Principal | findPrincipal(org.apache.catalina.connector.Request request, java.lang.String authorization, org.apache.catalina.Realm realm)Parse the specified authorization credentials, and return the
associated Principal that these credentials authenticate (if any)
from the specified Realm. If there is no such Principal, return
null .
//System.out.println("Authorization token : " + authorization);
// Validate the authorization credentials format
if (authorization == null)
return (null);
if (!authorization.startsWith("Digest "))
return (null);
authorization = authorization.substring(7).trim();
// Bugzilla 37132: http://issues.apache.org/bugzilla/show_bug.cgi?id=37132
String[] tokens = authorization.split(",(?=(?:[^\"]*\"[^\"]*\")+$)");
String userName = null;
String realmName = null;
String nOnce = null;
String nc = null;
String cnonce = null;
String qop = null;
String uri = null;
String response = null;
String method = request.getMethod();
for (int i = 0; i < tokens.length; i++) {
String currentToken = tokens[i];
if (currentToken.length() == 0)
continue;
int equalSign = currentToken.indexOf('=");
if (equalSign < 0)
return null;
String currentTokenName =
currentToken.substring(0, equalSign).trim();
String currentTokenValue =
currentToken.substring(equalSign + 1).trim();
if ("username".equals(currentTokenName))
userName = removeQuotes(currentTokenValue);
if ("realm".equals(currentTokenName))
realmName = removeQuotes(currentTokenValue, true);
if ("nonce".equals(currentTokenName))
nOnce = removeQuotes(currentTokenValue);
if ("nc".equals(currentTokenName))
nc = removeQuotes(currentTokenValue);
if ("cnonce".equals(currentTokenName))
cnonce = removeQuotes(currentTokenValue);
if ("qop".equals(currentTokenName))
qop = removeQuotes(currentTokenValue);
if ("uri".equals(currentTokenName))
uri = removeQuotes(currentTokenValue);
if ("response".equals(currentTokenName))
response = removeQuotes(currentTokenValue);
}
if ( (userName == null) || (realmName == null) || (nOnce == null)
|| (uri == null) || (response == null) )
return null;
// Second MD5 digest used to calculate the digest :
// MD5(Method + ":" + uri)
String a2 = method + ":" + uri;
//System.out.println("A2:" + a2);
byte[] buffer = null;
synchronized (md5Helper) {
buffer = md5Helper.digest(a2.getBytes());
}
String md5a2 = md5Encoder.encode(buffer);
return (realm.authenticate(userName, response, nOnce, nc, cnonce, qop,
realmName, md5a2));
| protected java.lang.String | generateNOnce(org.apache.catalina.connector.Request request)Generate a unique token. The token is generated according to the
following pattern. NOnceToken = Base64 ( MD5 ( client-IP ":"
time-stamp ":" private-key ) ).
long currentTime = System.currentTimeMillis();
String nOnceValue = request.getRemoteAddr() + ":" +
currentTime + ":" + key;
byte[] buffer = null;
synchronized (md5Helper) {
buffer = md5Helper.digest(nOnceValue.getBytes());
}
nOnceValue = md5Encoder.encode(buffer);
return nOnceValue;
| public java.lang.String | getInfo()Return descriptive information about this Valve implementation.
// ------------------------------------------------------------- Properties
return (info);
| protected java.lang.String | parseUsername(java.lang.String authorization)Parse the username from the specified authorization string. If none
can be identified, return null
//System.out.println("Authorization token : " + authorization);
// Validate the authorization credentials format
if (authorization == null)
return (null);
if (!authorization.startsWith("Digest "))
return (null);
authorization = authorization.substring(7).trim();
StringTokenizer commaTokenizer =
new StringTokenizer(authorization, ",");
while (commaTokenizer.hasMoreTokens()) {
String currentToken = commaTokenizer.nextToken();
int equalSign = currentToken.indexOf('=");
if (equalSign < 0)
return null;
String currentTokenName =
currentToken.substring(0, equalSign).trim();
String currentTokenValue =
currentToken.substring(equalSign + 1).trim();
if ("username".equals(currentTokenName))
return (removeQuotes(currentTokenValue));
}
return (null);
| protected static java.lang.String | removeQuotes(java.lang.String quotedString, boolean quotesRequired)Removes the quotes on a string. RFC2617 states quotes are optional for
all parameters except realm.
//support both quoted and non-quoted
if (quotedString.length() > 0 && quotedString.charAt(0) != '"" &&
!quotesRequired) {
return quotedString;
} else if (quotedString.length() > 2) {
return quotedString.substring(1, quotedString.length() - 1);
} else {
return new String();
}
| protected static java.lang.String | removeQuotes(java.lang.String quotedString)Removes the quotes on a string.
return removeQuotes(quotedString, false);
| protected void | setAuthenticateHeader(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response, org.apache.catalina.deploy.LoginConfig config, java.lang.String nOnce)Generates the WWW-Authenticate header.
The header MUST follow this template :
WWW-Authenticate = "WWW-Authenticate" ":" "Digest"
digest-challenge
digest-challenge = 1#( realm | [ domain ] | nOnce |
[ digest-opaque ] |[ stale ] | [ algorithm ] )
realm = "realm" "=" realm-value
realm-value = quoted-string
domain = "domain" "=" <"> 1#URI <">
nonce = "nonce" "=" nonce-value
nonce-value = quoted-string
opaque = "opaque" "=" quoted-string
stale = "stale" "=" ( "true" | "false" )
algorithm = "algorithm" "=" ( "MD5" | token )
// Get the realm name
String realmName = config.getRealmName();
if (realmName == null)
realmName = request.getServerName() + ":"
+ request.getServerPort();
byte[] buffer = null;
synchronized (md5Helper) {
buffer = md5Helper.digest(nOnce.getBytes());
}
String authenticateHeader = "Digest realm=\"" + realmName + "\", "
+ "qop=\"auth\", nonce=\"" + nOnce + "\", " + "opaque=\""
+ md5Encoder.encode(buffer) + "\"";
response.setHeader("WWW-Authenticate", authenticateHeader);
|
|