| Method from org.apache.catalina.authenticator.DigestAuthenticator Detail: |
public boolean authenticate(Request request,
Response response,
LoginConfig config) throws IOException {
// 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);
}
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. |
protected static Principal findPrincipal(Request request,
String authorization,
Realm realm) {
//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));
}
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. |
protected String generateNOnce(Request request) {
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;
}
Generate a unique token. The token is generated according to the
following pattern. NOnceToken = Base64 ( MD5 ( client-IP ":"
time-stamp ":" private-key ) ). |
public String getInfo() {
// ------------------------------------------------------------- Properties
return (info);
}
Return descriptive information about this Valve implementation. |
protected String parseUsername(String authorization) {
//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);
}
Parse the username from the specified authorization string. If none
can be identified, return null |
protected static String removeQuotes(String quotedString) {
return removeQuotes(quotedString, false);
}
Removes the quotes on a string. |
protected static String removeQuotes(String quotedString,
boolean quotesRequired) {
//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();
}
}
Removes the quotes on a string. RFC2617 states quotes are optional for
all parameters except realm. |
protected void setAuthenticateHeader(Request request,
Response response,
LoginConfig config,
String nOnce) {
// 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);
}
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 )
|