public final class DigestAuthentication
extends java.lang.Object
Create an instance of this class once a digest challenge is received. Use the instance to respond to the challenge and to authenticate future requests.
HttpURLConnection
and, in case
of a challenge, respond to the challenge:
// Step 1. Create the connection URL url = new URL("http://httpbin.org/digest-auth/auth/user/passwd"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); // Step 2. Make the request and check to see if the response contains an authorization challenge if (connection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) { // Step 3. Create a authentication object from the challenge... DigestAuthentication auth = DigestAuthentication.fromResponse(connection); // ...with correct credentials auth.username("user").password("passwd"); // Step 4 (Optional). Check if the challenge was a digest challenge of a supported type if (!auth.canRespond()) { // No digest challenge or a challenge of an unsupported type - do something else or fail return; } // Step 5. Create a new connection, identical to the original one. connection = (HttpURLConnection) url.openConnection(); // ...and set the Authorization header on the request, with the challenge response connection.setRequestProperty(DigestChallengeResponse.HTTP_HEADER_AUTHORIZATION, auth.getAuthorizationForRequest("GET", connection.getURL().getPath())); }
DigestAuthentication
object can be used to authenticate future requests. This removes
the need of making the request twice (once for the challenge and once for the actual request):
HttpURLConnection anotherConnection = (HttpURLConnection) url.openConnection(); anotherConnection.setRequestProperty(DigestChallengeResponse.HTTP_HEADER_AUTHORIZATION, auth.getAuthorizationForRequest("GET", initialConnection.getURL().getPath()));
WwwAuthenticateHeader
to
parse WWW-Authenticate
headers into individual challenges. Example:
if (connection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) { // Parse the headers and extract challenges, this will return challenges of all types List<String> challengeStrings = WwwAuthenticateHeader.extractChallenges(connection.getHeaderFields()); // Check the challenges and act on them... // ...or pass them to DigestAuthentication to handle digest challenges: DigestAuthentication auth = DigestAuthentication.fromChallenges(challengeStrings).username("user").password ("passwd"); }
challengeOrdering(Comparator)
to define
which challenge is preferred.
auth-int
quality of protectionauth-int
quality of protection requires a digest of the entity body of the
request to be included in the challenge response. To be compatible with servers that require
auth-int
quality of protection, use
getAuthorizationForRequest(String, String, byte[])
instead of
getAuthorizationForRequest(String, String)
.
auth-int
is uncommon. It cannot be used with HTTP requests that does not include a
body, such as GET
. Some server implementations accept auth-int
authentication for such requests as well, using a zero-length entity body.
Modifier and Type | Field and Description |
---|---|
static java.util.Comparator<DigestChallenge> |
DEFAULT_CHALLENGE_COMPARATOR
Default comparator used when comparing which challenge to use when there is more than one to
choose from.
|
Modifier and Type | Method and Description |
---|---|
boolean |
canRespond()
Returns
true if a response can be generated to any of the challenges. |
DigestAuthentication |
challengeOrdering(java.util.Comparator<? super DigestChallenge> orderingComparator)
Sets the challenge ordering, which will determine which challenge that will be used if there
are several.
|
static DigestAuthentication |
fromChallenges(java.lang.Iterable<java.lang.String> challenges)
Creates an authentication from a number of challenges.
|
static DigestAuthentication |
fromDigestChallenge(DigestChallenge challenge)
Creates an authentication from a parsed Digest challenge.
|
static DigestAuthentication |
fromDigestChallenges(java.lang.Iterable<? extends DigestChallenge> challenges)
Creates an authentication from a number of parsed Digest challenges.
|
static DigestAuthentication |
fromResponse(java.net.HttpURLConnection connection)
Creates an authentication by reading response headers from an
HttpURLConnection object. |
static <T extends java.lang.Iterable<java.lang.String>> |
fromResponseHeaders(java.util.Map<java.lang.String,T> headers)
Creates an authentication from a map of HTTP response headers.
|
static DigestAuthentication |
fromWwwAuthenticateHeader(java.lang.String wwwAuthenticateHeader)
Creates an authentication from a single
WWW-Authenticate header. |
static DigestAuthentication |
fromWwwAuthenticateHeaders(java.lang.Iterable<java.lang.String> wwwAuthenticateHeaders)
Creates an authentication from a number of
WWW-Authenticate headers. |
java.lang.String |
getAuthorizationForRequest(java.lang.String requestMethod,
java.lang.String digestUri)
Returns the value of
Authorization header that can be used in a particular
request. |
java.lang.String |
getAuthorizationForRequest(java.lang.String requestMethod,
java.lang.String digestUri,
byte[] entityBody)
Returns the value of
Authorization header that can be used in a particular
request. |
DigestChallengeResponse |
getChallengeResponse()
Picks a challenge among the available challenges and generates a response to it.
|
java.lang.String |
getPassword()
Returns the password to use for authentication.
|
java.lang.String |
getUsername()
Returns the username to use for authentication.
|
boolean |
isEntityBodyDigestRequired()
Returns
true if the digest of the entity-body is required to generate a
response to the preferred challenge. |
DigestAuthentication |
password(java.lang.String password)
Sets the password to use for authentication.
|
java.lang.String |
toString() |
DigestAuthentication |
username(java.lang.String username)
Sets the username to use for authentication.
|
public static final java.util.Comparator<DigestChallenge> DEFAULT_CHALLENGE_COMPARATOR
Challenges that are not supported are ordered last.
Challenges that use digest algorithms SHA-256
or SHA-256-sess
are
always preferred over challenges that use MD5
, MD5-sess
, or does
not specify an algorithm.
If the rules above are not enough challenges are ordered according to the following, from most preferred to least preferred:
auth
and
auth-int
.auth
but not
auth-int
.auth-int
and nothing else.
This is ranked last among the supported challenges because auth-int
is limited
and cannot authenticate requests without a body, such as HTTP GET.public static DigestAuthentication fromResponse(java.net.HttpURLConnection connection) throws ChallengeParseException
HttpURLConnection
object.connection
- the connectionDigestAuthentication
objectChallengeParseException
- if challenges could not be parsedpublic static <T extends java.lang.Iterable<java.lang.String>> DigestAuthentication fromResponseHeaders(java.util.Map<java.lang.String,T> headers) throws ChallengeParseException
A note about the map representing the headers: header names are case insensitive in HTTP, but
keys in a Map
are case-sensitive. This method uses
WwwAuthenticateHeader.extractChallenges(Map)
and handles case in the same way.
headers
- the headers, as a map where the keys are header names and values are
iterables where each element is a header value stringDigestAuthentication
objectChallengeParseException
- if challenges could not be parsedpublic static DigestAuthentication fromWwwAuthenticateHeaders(java.lang.Iterable<java.lang.String> wwwAuthenticateHeaders) throws ChallengeParseException
WWW-Authenticate
headers.wwwAuthenticateHeaders
- the WWW-Authenticate
headersDigestAuthentication
objectChallengeParseException
- if challenges could not be parsedpublic static DigestAuthentication fromWwwAuthenticateHeader(java.lang.String wwwAuthenticateHeader) throws ChallengeParseException
WWW-Authenticate
header.wwwAuthenticateHeader
- the WWW-Authenticate
headerDigestAuthentication
objectChallengeParseException
- if challenges could not be parsedpublic static DigestAuthentication fromChallenges(java.lang.Iterable<java.lang.String> challenges) throws ChallengeParseException
Challenges can be of any type, not just Digest challenges. See RFC 7235, Section 2.1 for a definition of challenges. Some example of valid challenges:
Basic realm="simple"
Digest realm="testrealm@host.com", qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
Newauth realm="apps", type=1, title="Login to \"apps\""
challenges
- the challengesDigestAuthentication
objectChallengeParseException
- if challenges could not be parsedpublic static DigestAuthentication fromDigestChallenges(java.lang.Iterable<? extends DigestChallenge> challenges)
challenges
- the digest challengesDigestAuthentication
objectpublic static DigestAuthentication fromDigestChallenge(DigestChallenge challenge)
challenge
- the digest challengeDigestAuthentication
objectpublic boolean canRespond()
true
if a response can be generated to any of the challenges.
If this method returns false
, it could mean that:
DigestChallengeResponse.isChallengeSupported(DigestChallenge)
).true
if a response can be generated to any of the challengespublic DigestAuthentication challengeOrdering(java.util.Comparator<? super DigestChallenge> orderingComparator)
By default, challenges are sorted using DEFAULT_CHALLENGE_COMPARATOR
.
This method must be called before a choice is made as to which challenge to use. Once a choice
has been made it cannot be changed. This means that his method cannot be called after methods
such as getChallengeResponse()
or getAuthorizationForRequest(String, String)
.
orderingComparator
- A comparator object that will be used to sort the challenges. The
challenge that will be used is the first supported challenge
according to the sort order defined by the comparator.java.lang.IllegalStateException
- if this method is called after a method that requires a choice
to be made regarding which of the available challenges to use:
isEntityBodyDigestRequired()
,
getChallengeResponse()
,
getAuthorizationForRequest(String, String)
,
getAuthorizationForRequest(String, String, byte[])
.public boolean isEntityBodyDigestRequired()
true
if the digest of the entity-body
is required to generate a
response to the preferred challenge.
For most challenges, setting the digest of the entity-body
is optional. It is only
required if the only quality of protection the server accepts is auth-int
.
true
if the digest of the entity-body
must be setpublic DigestAuthentication username(java.lang.String username)
username
- the usernamegetUsername()
,
Section 3.2.2 of RFC 2617public java.lang.String getUsername()
username(String)
public DigestAuthentication password(java.lang.String password)
password
- the passwordgetPassword()
public java.lang.String getPassword()
password(String)
public DigestChallengeResponse getChallengeResponse()
The response returned references the internal representation of this instance, modifying it
will modify this instance. Example: Calling
username
on the response will change the value
returned by getUsername()
.
java.lang.IllegalStateException
- if this method is called when
canRespond()
returns false
, that is, none of
the available challenges are supportedpublic java.lang.String getAuthorizationForRequest(java.lang.String requestMethod, java.lang.String digestUri)
Authorization
header that can be used in a particular
request.
The first time an Authorization
header is generated nonce count will be set to 1.
Each subsequent call will increase the nonce count by one. The server expects the nonce count
to increase by exactly one for each request, so do not call this method unless you intend to
use the result in a request.
Calling this method has the same effect as calling
getAuthorizationForRequest(String, String, byte[])
with a zero-length byte array for
entityBody
.
requestMethod
- the HTTP request method, such as GET or POST.digestUri
- the Request-URI
of the Request-Line
of the HTTP request,
see DigestChallengeResponse.digestUri(String)
for a discussion
of what to set hereAuthorization
headerjava.lang.IllegalStateException
- If this method is called when
canRespond()
returns false
, that
is, none of the available challenges are supportedInsufficientInformationException
- If username or password has not been setDigestChallengeResponse.requestMethod(String)
,
DigestChallengeResponse.digestUri(String)
,
getAuthorizationForRequest(String, String, byte[])
public java.lang.String getAuthorizationForRequest(java.lang.String requestMethod, java.lang.String digestUri, byte[] entityBody)
Authorization
header that can be used in a particular
request.
This method takes the request's entity-body
as an argument. The entity body is the
message body after decoding any transfer encoding that might have been applied. Example: If
Transfer-Encoding
is gzip
the entity body is the unzipped message and
the message body is the gzipped message. Only some requests have entity bodies,
GET
requests for example do not. See
DigestChallengeResponse.entityBody(byte[])
for more details.
This method can be used for any request, but the entity-body is only used for "quality of
protection" auth-int
. Quality of protection auth-int
requires a
hash of the entity body of the message to be included in the challenge response.
Not all requests have an entity-body
, for example, GET requests do not. Some
servers accept an entity-body
of zero length for such requests (even though it is
strictly speaking not correct to do so).
The first time an Authorization
header is generated nonce count will be set to 1.
Each subsequent call will increase the nonce count by one. The server expects the nonce count
to increase by exactly one for each request, so do not call this method unless you intend to
use the result in a request.
requestMethod
- the HTTP request method, such as GET or POST.digestUri
- the Request-URI
of the Request-Line
of the HTTP request,
see DigestChallengeResponse.digestUri(String)
for a discussion
of what to set hereentityBody
- the entity-body
of the request (see above)Authorization
headerjava.lang.IllegalStateException
- If this method is called when
canRespond()
returns false
, that
is, none of the available challenges are supportedInsufficientInformationException
- If username or password has not been setDigestChallengeResponse.requestMethod(String)
,
DigestChallengeResponse.digestUri(String)
,
DigestChallengeResponse.entityBody(byte[])
,
getAuthorizationForRequest(String, String)
,
isEntityBodyDigestRequired()
public java.lang.String toString()
toString
in class java.lang.Object