Can Java's 'single sign-on' (use crede

2020-01-30 11:30发布

Oracle's "Http Authentication" page from the Java SE 6 documentation says that "if you are running on a Windows machine as a domain user, or, you are running on a Linux or Solaris machine that has already issued the kinit command and got the credential cache" then the instance passed to Authenticator.setDefault() "will be completely ignored".

This matches what I observed: setting up an HTTP or HTTPS connection on a Windows system to host X always passes the credentials for host X from the 'Windows Credentials' of the 'Windows Vault', as seen in my Windows 7 'Credential Manager' Control Panel page.

However, in my use case I don't want to use any credentials which might be stored by Windows, but instead I always want to use credentials I explicitly specify in the code.

Is there a way to override the documented behavior, i.e., is there a way to ignore the credentials stored by Windows?

Update: If not, could someone point me to a place in the Java SE 6 source code where I can see that the stored Windows credentials cannot be ignored?

4条回答
家丑人穷心不美
2楼-- · 2020-01-30 12:08

I've looked for the same thing you are asking. So far, I haven't found a way on the JDK to do that.

There is a request for enhancement on Java Bug Database. Take a look at the report to find out if that gets a response from Sun (vote up the report so that hopefully that gets fixed soon).

What I ended up doing, was override sun.net.www.protocol.http.NTLMAuthentication class. By looking at sun.net.www.protocol.http.HttpURLAuthentication, I found that the only thing you need to modify is the result of:

NTLMAuthentication.supportsTransparentAuth()

That method has a hardcoded return value, true on Windows platforms and false otherwise. This code is extracted from a JDK installed on Windows 7:

static boolean supportsTransparentAuth()
{
  return true;
}

What that method tells is if Windows credentials should be used by default. If set to true, your custom Authenticator code won't be called. See this fragment of HttpURLConnection class:

//Declared as a member variable of HttpURLConnection
private boolean tryTransparentNTLMServer = NTLMAuthentication.supportsTransparentAuth();

//Inside of getServerAuthentication method.
PasswordAuthentication a = null;
if (!tryTransparentNTLMServer) {
    //If set to false, this will call Authenticator.requestPasswordAuthentication().
    a = privilegedRequestPasswordAuthentication(url.getHost(), addr, port, url.getProtocol(), "", scheme, url, RequestorType.SERVER);
}

/* If we are not trying transparent authentication then 
* we need to have a PasswordAuthentication instance. For
* transparent authentication (Windows only) the username 
* and password will be picked up from the current logged 
* on users credentials.
*/
if (tryTransparentNTLMServer || (!tryTransparentNTLMServer && a != null)) {
    //If set to true or if Authenticator did not return any credentials, use Windows credentials.
    //NTLMAuthentication constructor, if receives a == null will fetch current looged user credentials.
    ret = new NTLMAuthentication(false, url1, a);
}

To get NTLMAuthentication source code, I used this Java decompiler. Opened rt.jar located on the JDK installation folder and copied the desired class code.

Then, I simply changed supportsTransparentAuth to return false. However, it would be highly desirable if this method checked first a system property and then return true or false based on that.

To compile it, I just placed the java file under sun/net/www/protocol/http folder structure and run:

javac NTLMAuthentication.java

Then run my application using:

java -Xbootclasspath:"path/to/your/sun/net/www/protocol/http/classes;normal/JDK/boot/directories"

That will tell the JVM to load our implementation of NTLMAuthentication before the one in rt.jar. You have to be careful to don't miss any default class loading paths with -Xbootclasspath, or there will be ClassNotFound errors.

After that, everything worked just fine.

This approach has important drawbacks that you should be aware of.

  • There are security risks. Anyone could drop a different .class file on your boot folder and steal the user credentials or other important information.
  • Code from Sun packages can change without notice and thus be incompatible with your changes.
  • If you deploy this code, you will be contravening the Sun code license. From the documentation:

-Xbootclasspath:bootclasspath Specify a semicolon-separated list of directories, JAR archives, and ZIP archives to search for boot class files. These are used in place of the boot class files included in the Java 2 SDK. Note: Applications that use this option for the purpose of overriding a class in rt.jar should not be deployed as doing so would contravene the Java 2 Runtime Environment binary code license.

So, this is definitely not suitable for production environments.

Finally, this is an excellent source about boot class path option and Java class loaders: PDF

Hope this helps.

查看更多
霸刀☆藐视天下
3楼-- · 2020-01-30 12:11

At least in Java 7 there is a class called sun.net.www.protocol.http.ntlm.NTLMAuthenticationCallback that seems to help with this situation. Single sign-on is only invoked for "trusted" URLs.

Here is the simplest implementation to turn it off (have this initialiser called prior to opening the HTTP connection):

static {
    NTLMAuthenticationCallback.setNTLMAuthenticationCallback(new NTLMAuthenticationCallback()
    {
        @Override
        public boolean isTrustedSite(URL url)
        {
            return false;
        }
    });
}

I guess the default implementation is to trust everything :(

查看更多
小情绪 Triste *
4楼-- · 2020-01-30 12:14

With Java v8 212 or higher, "transparent" SSO authentication via NTLMv2 is disabled by default, as a consequence of this CVE: https://nvd.nist.gov/vuln/detail/CVE-2019-2426. For more information: How to provide ntlm authentication while calling any url?

查看更多
Ridiculous、
5楼-- · 2020-01-30 12:16

It seems that the class sun.net.www.protocol.http.ntlm.NTLMAuthenticationCallback was added to java 6.0 patch 24+ so the solution suggested can work in java 6.0 as well. See reference in the following post: http://www.mail-archive.com/users@cxf.apache.org/msg22897.html

查看更多
登录 后发表回答