I've written a simple Java HTTP Client that is running under Windows. The client communicates with a web server which requires Kerberos authentication through SPNego.
I'm experiencing two problems:
The service ticket is not stored in my credentials cache. After performing a request, I expected to see a Kerberos Service Ticket stored in my credentials cache under C:\Users\\krb5cc_ - was I wrong to assume that Java stores service tickets in the credential cache? I'd like to reuse a Service Ticket obtained in Client A for requests in Client B (where both Clients are Java applications on the same machine). Is this possible with Java?
If I run the code below one hundred times in a loop, it only works n-times (where n is a random number between 1 and 100). The failing request returns a 401 error message, because Java wasn't able to retrieve a Service ticket (remember: since my application doesn't store service tickets between requests, it tries to obtain a new service ticket from the TGT for every request). I've added the error message to the bottom of this question.
I've created a TGT via kinit in my JDK's bin folder. The following code snippet is used for making simple GET requests:
static void testJavaHttpKerberosAuthentication() throws IOException {
URL obj = new URL(URI);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
int responseCode = con.getResponseCode();
System.out.println("\nSending 'GET' request to URL : " + URI);
System.out.println("Response Code : " + responseCode);
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
//print result
System.out.println(response.toString());
}
Here's the content of my jaas.conf (as described here):
com.sun.security.jgss.krb5.initiate {
com.sun.security.auth.module.Krb5LoginModule required doNotPrompt=false useTicketCache=true;
};
I'm running my application with the following parameters:
-Djava.security.auth.login.config=D:\jaas.conf
-Dsun.security.krb5.debug=true
-Djavax.security.auth.useSubjectCredsOnly=false
I'm not using as a krb5.ini since my client obtains the correct KDC from the domain configuration.
I can generate a TGT for my credentials cache via the following command:
C:\Program Files\Java\jdk1.8.0_77\bin>kinit
Password for @:
New ticket is stored in cache file C:\Users\\krb5cc_
And finally, here's the exception and Kerberos Debug Output for the case where authorization fails (ref. Problem 2). Please note that ctime is obviously wrong. I've had many different attempts and the timespan for the ctime ranges from 1970 to 2040. Interestingly enough, this doesn't happen for every request.
>>>KRBError:
cTime is Wed Jun 07 12:24:03 CEST 2017 1496831043000
sTime is Tue Mar 29 16:38:24 CEST 2016 1459262304000
suSec is 283371
error code is 34
error Message is Request is a replay
sname is HTTP/@
msgType is 30
KrbException: Request is a replay (34) - PROCESS_TGS
I've already tried to work with JAAS using Subject.doAs, but this is causing the same problems. Accesing the server via the browser works fine (although this is not comparable, as the browsers are using the Windows native credentials cache AFAICT).
I'd be thankful for some advice on how to debug a problem like this.
EDIT: Specifiying the path to the credentials cache via the KRB5CCNAME environment variable explicitly, does not change the behavior. It seems like the TGT is obtained from the Credentials Cache but Service Tickets are not stored there.
解决方案
JAAS is not going to persist tickets into the cache, you have to use kinit or invoke kinit code programmatically by your code.
I have wrote a SO question/answer on this problem here.