Reset the Authenticator credentials
Asked Answered
C

4

9

We have a static method in a utility class that will download a file from a URL. An authenticator has been set up so that if a username and password is required, the credentials can be retrieved. The problem is that the credentials from the first successful connection are used for every connection afterwords, as long as the credentials are valid. This is a problem because our code is multi user, and since the credentials are not checked for every connection, it's possible that a user without proper credentials could download a file.

Here's the code we're using

private static URLAuthenticator auth;

public static File download(String url, String username, String password, File newFile)
{
    auth.set(username, password);
    Authenticator.setDefault(auth);
    URL fURL = new URL(url);
    OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile));
    URLConnection conn = fURL.openConnection();
    InputStream in = conn.getInputStream();

    try
    {
        copyStream(in, out);
    }
    finally
    {
        if (in != null)
            in.close();
        if (out != null)
            out.close();
    }

    return newFile;
}

public class URLAuthenticator extends Authenticator
{
    private String username;
    private String password;

    public URLAuthenticator(String username, String password)
    {
         set(username, password);
    }

    public void set(String username, String password)
    {
        this.username = username;
        this.password = password;
    }

    protected PasswordAuthentication getPasswordAuthentication()
    {
        log.debug("Retrieving credentials '" + username + "', '" + password + "'.");
        return new PasswordAuthentication(username, password.toCharArray());
    }
}

I only see the log statement from getPasswordAuthentication once, the first time that a file is downloaded. After that first successful attempt, getPasswordAuthentication is not called again, even though the credentials have been reset. The result is that after the first successful connection, invalid credentials can be entered, and a successful connection can still be made. Is this possibly a result of the download method being static, and in a static class?

Edit I forgot to mention that this is in a JSF webapp running under tomcat - maybe one of those technologies is setting some default credentials somewhere?

I've pulled the URLAuthenticator out into its own class, and made it as non-static as possible, but the problem still exists. I've read that if the default authenticator is set to null with Authenticator.setDefault(null), then on windows the NTLM authentication will be used. That shouldn't be the problem here since I'm setting the Authenticator everytime, but I thought I'd throw it out there. The NTLM authentication is definately getting used, because if the server is run as a user that has access to the downloaded file, the credentials aren't even asked for, the file just downloads. So something obviously is grabbing my credentials and passing them in before the authenticator is called.

Carisacarissa answered 26/1, 2009 at 18:46 Comment(0)
C
11

I've figured something out at least. It appears that this behavior is a bug. A workaround is to use a Sun specific class to explicitly reset the cache, like so:

import sun.net.www.protocol.http.AuthCacheValue;
import sun.net.www.protocol.http.AuthCacheImpl;
....
AuthCacheValue.setAuthCache(new AuthCacheImpl());
Authenticator.setDefault(new URLAuthenticator(username, password));

I'm resetting the AuthCache at the top of the download function described in the question. During compile, you'll get warnings about using these classes. This doesn't completely fix the problem: if NTLM authentication works, the authenticator still won't get called, but as long as the server is running under a user that has does not have permission for the requested file, this should clear the cache out.

Carisacarissa answered 28/1, 2009 at 15:12 Comment(3)
I know it's been a long long time, but I just encountered the same issue. Did you even figure out a more elegant fix???Indefinable
Nope, I never did - IIRC this is the solution we ended up having to go with. Luckily (for us) this was never put in to production.Carisacarissa
@MattMcMinn This cache issue is only with sun.net.www.protocol.http.HttpURLConnection correct ? if I am using java.net.HttpURLConnection this cache issue wont have I believe!!Spunky
L
5

Facing the same problem, none of these answers worked for me. It took me some time and looking through the java runtime source to figure this out. Sun.net.www.protocol.http.ntlm.NTLMAuthentication attempts to use transparent authentication, what is basically use of current user credentials to login to remote server. In my server to server scenario (Java EE server to Sharepoint) this wasn't acceptable. In order to disable transparent authentication we need to let authentication provider know that connection is not trusted and it needs to authenticate with every call:

static {
    NTLMAuthenticationCallback.setNTLMAuthenticationCallback(new NTLMAuthenticationCallback()
    {
        @Override
        public boolean isTrustedSite(URL url)
        {
            return false;
        }
    });
}        
Lenz answered 9/4, 2013 at 14:15 Comment(3)
hallo, this sounds promising. do you have some example how to call this from. thx a lot for thisInerrant
You don't call this, it is override for the method inside Java runtime. Internal NTLM authentication routine invokes isTrustedSite that returns false, and than it always invokes custom Authenticator for credentials. For sample Authenticator see previous posts.Lenz
Note that NTLMAuthenticationCallback is a hidden internal sun.* class, so you will have to provide the "-XDignore.symbol.file" argument to javac to compile it.Emblematize
F
3

Here is the working( i.e., getPasswordAuthentication() called for every request) code. You will see compilation warning which can be ignored.

static class MyCache implements sun.net.www.protocol.http.AuthCache{
     public void put(String pkey, sun.net.www.protocol.http.AuthCacheValue value){

     }
     public sun.net.www.protocol.http.AuthCacheValue get(String pkey, String skey){
         return null;
     }
     public void remove(String pkey, sun.net.www.protocol.http.AuthCacheValue entry){

     }
}
static{
    sun.net.www.protocol.http.AuthCacheValue.setAuthCache(new MyCache());

}
Faitour answered 18/5, 2011 at 19:15 Comment(0)
H
2

It looks like in your finally block you need to call

Authenticator.setDefault(null);
Haberdasher answered 26/1, 2009 at 19:0 Comment(3)
Weird, that looked like it from the Java Docs. Have you tried getting rid of the static UrlAuthenticator? Not sure why that would need to be static.Haberdasher
I've refactored so that it's not static, and pulled the URLAuthenticator into its own class, but the problem is still there.Carisacarissa
Ok, I'm at a loss on this. The only (bad) idea that I have is instead of setting setDefault to null to assign it a bad (won't validate) Authenticator. Sorry I don't have any decent ideas.Haberdasher

© 2022 - 2024 — McMap. All rights reserved.