DigestAuthenticatorpublic class DigestAuthenticator extends AuthenticatorBase An Authenticator and Valve implementation of HTTP DIGEST
Authentication (see RFC 2069). |
Fields Summary |
---|
protected static final int | USE_ONCEIndicates that no once tokens are used only once. | protected static final int | USE_NEVER_EXPIRESIndicates that no once tokens are used only once. | protected static final int | TIMEOUT_INFINITEIndicates that no once tokens are used only once. | 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 Hashtable | nOnceTokensNo once hashtable. | protected long | nOnceTimeoutNo once expiration (in millisecond). A shorter amount would mean a
better security level (since the token is generated more often), but at
the expense of a bigger server overhead. | protected int | nOnceUsesNo once expiration after a specified number of uses. A lower number
would produce more overhead, since a token would have to be generated
more often, but would be more secure. | 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.HttpRequest request, org.apache.catalina.HttpResponse 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 =
((HttpServletRequest) request.getRequest()).getUserPrincipal();
if (principal != null)
return (true);
// Validate any credentials already included with this request
HttpServletRequest hreq =
(HttpServletRequest) request.getRequest();
HttpServletResponse hres =
(HttpServletResponse) response.getResponse();
String authorization = request.getAuthorization();
if (authorization != null) {
principal = findPrincipal(hreq, authorization, context.getRealm());
if (principal != null) {
String username = parseUsername(authorization);
register(request, response, principal,
Constants.DIGEST_METHOD,
username, null);
String ssoId = (String) request.getNote(
Constants.REQ_SSOID_NOTE);
if (ssoId != null) {
getSession(request, true);
}
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(hreq);
setAuthenticateHeader(hreq, hres, config, nOnce);
hres.sendError(HttpServletResponse.SC_UNAUTHORIZED);
// hres.flushBuffer();
return (false);
| protected static java.security.Principal | findPrincipal(javax.servlet.http.HttpServletRequest 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();
StringTokenizer commaTokenizer =
new StringTokenizer(authorization, ",");
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 opaque = null;
String method = request.getMethod();
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))
userName = removeQuotes(currentTokenValue);
if ("realm".equals(currentTokenName))
realmName = removeQuotes(currentTokenValue, true);
if ("nonce".equals(currentTokenName))
nOnce = removeQuotes(currentTokenValue);
if ("nc".equals(currentTokenName))
nc = 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(javax.servlet.http.HttpServletRequest 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 = md5Helper.digest(nOnceValue.getBytes());
nOnceValue = md5Encoder.encode(buffer);
// Updating the value in the no once hashtable
nOnceTokens.put(nOnceValue, Long.valueOf(currentTime + nOnceTimeout));
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.
//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(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse 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);
|
|