133

How do I connect to a remote URL in Java which requires authentication. I'm trying to find a way to modify the following code to be able to programatically provide a username/password so it doesn't throw a 401.

URL url = new URL(String.format("http://%s/manager/list", _host + ":8080"));
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
Matteo
  • 13,467
  • 9
  • 63
  • 103
user14128
  • 2,419
  • 4
  • 19
  • 16

12 Answers12

138

You can set the default authenticator for http requests like this:

Authenticator.setDefault (new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication ("username", "password".toCharArray());
    }
});

Also, if you require more flexibility, you can check out the Apache HttpClient, which will give you more authentication options (as well as session support, etc.)

James Van Huis
  • 5,333
  • 1
  • 24
  • 25
  • 4
    How do you handle a bad authentication event? [For example, if the user supplies username and password authentication credentials that don't match anything]? – SK9 Aug 14 '11 at 06:06
  • 2
    The above code works but is quite implicit as to whats going on. There's subclassing and method overriding going on there, dig into the docs for those classes if you care to know what's going on. The code here is more explicit [javacodegeeks](http://examples.javacodegeeks.com/core-java/net/authenticator/access-password-protected-url-with-authenticator/) – Fuchida May 07 '14 at 17:01
  • 13
    Is it possible to set specific `Authenticator` per specific `URL.openConnection()` invocation rather than setting it globally? – Yuriy Nakonechnyy Oct 29 '15 at 13:23
  • @Yura: no. It's got to be global. You can, however, do evil things such as setting a global authenticator which pulls the credentials out of thread-local variables, and set the credentials per thread before making the HTTP connection. – David Given May 29 '17 at 14:46
  • You can do this: `connection.setAuthenticator(...)` – Punyapat Jul 19 '18 at 11:12
  • The answer by @gloo is more appropriate. It doesn't have wide system impact as it doesn't set a global Authenticator, nor does it require any external packages. – jmng Oct 28 '18 at 18:26
  • 4
    In response to @YuriyNakonechnyy... if you are using Java 9 or above then you can call `HttpURLConnection.setAuthenticator()`. Unfortunately on Java 8 and earlier, the Authenticator instance is a JVM-wide global variable. See: https://docs.oracle.com/javase/9/docs/api/java/net/HttpURLConnection.html#setAuthenticator-java.net.Authenticator- – Neil Bartlett Aug 09 '19 at 14:17
  • Thanks @NeilBartlett - that's useful comment! – Yuriy Nakonechnyy Aug 19 '19 at 17:28
136

There's a native and less intrusive alternative, which works only for your call.

URL url = new URL(“location address”);
URLConnection uc = url.openConnection();
String userpass = username + ":" + password;
String basicAuth = "Basic " + new String(Base64.getEncoder().encode(userpass.getBytes()));
uc.setRequestProperty ("Authorization", basicAuth);
InputStream in = uc.getInputStream();
Daniel
  • 25,883
  • 17
  • 87
  • 130
Wanderson Santos
  • 2,729
  • 1
  • 22
  • 16
  • 5
    The Base64 class can be provided by Apache Commons Codec. – Matthew Buckett May 09 '11 at 11:45
  • Didn't work for me this way... Only the way that @James Van Huis was good – Miguel Ribeiro Aug 16 '11 at 16:32
  • It's 'native' on Grails and many other Java frameworks because them all use Apache Commons Libs. – Wanderson Santos Sep 02 '12 at 03:53
  • 1
    Does not work in general unless you `.trim()` the result, or call a method variant that does not produce chunked output. `javax.xml.bind.DatatypeConverter` seems safer. – Jesse Glick May 09 '13 at 15:00
  • I quoted your code in [this answer](http://stackoverflow.com/a/34319667/5330578) Thank you – notes-jj Dec 16 '15 at 18:40
  • Shouldn't you be storing the password in a `char[]`? http://stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords#8881376 – Brian McCutchon Feb 19 '17 at 17:59
  • The native Base64 encoding was flawed until Java 8 so it might explain why it wasn't working on some configurations before. – Matthieu Jan 04 '18 at 02:12
  • @BrianMcCutchon yes if you're using it internally for some other purpose. But as i's probably stored somewhere in plain text... You could use it as a `byte[]` though, along with the username and pass the whole thing to Base64 encoding to make it more "secure". – Matthieu Jan 04 '18 at 02:15
  • Does anybody know how can I connect and create a folder on the remote machine? I am getting to connect with success but I still need to create a folder that doesn´t exist yet. – Francisco Souza Feb 04 '21 at 18:51
80

You can also use the following, which does not require using external packages:

URL url = new URL(“location address”);
URLConnection uc = url.openConnection();

String userpass = username + ":" + password;
String basicAuth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(userpass.getBytes());

uc.setRequestProperty ("Authorization", basicAuth);
InputStream in = uc.getInputStream();
Stephan
  • 37,597
  • 55
  • 216
  • 310
gloo
  • 921
  • 7
  • 6
  • 11
    I've been looking for a Base64 encoder inside java standard packages for so long ! Thank you – qwertzguy Nov 27 '12 at 10:31
  • Note that for Java9+ you'll probably have to `--add-modules javax.xml.bind` since they removed the package from the default classpath. And in Java11+ it's removed altogether, you need an external dependency again. Such is progress! – Ed Randall Aug 18 '19 at 07:01
  • 1
    @EdRandall there's a `java.util.Base64` now so it is progress indeed :) – Steven Schlansker Dec 30 '19 at 23:37
45

If you are using the normal login whilst entering the username and password between the protocol and the domain this is simpler. It also works with and without login.

Sample Url: http://user:pass@domain.com/url

URL url = new URL("http://user:pass@domain.com/url");
URLConnection urlConnection = url.openConnection();

if (url.getUserInfo() != null) {
    String basicAuth = "Basic " + new String(new Base64().encode(url.getUserInfo().getBytes()));
    urlConnection.setRequestProperty("Authorization", basicAuth);
}

InputStream inputStream = urlConnection.getInputStream();

Please note in the comment, from valerybodak, below how it is done in an Android development environment.

javabeangrinder
  • 5,961
  • 5
  • 28
  • 37
  • 1
    This was *exactly* what I've been searching for the past 30 minutes. Thanks a lot! – Geert Schuring Nov 03 '14 at 15:09
  • If your username/password contain special characters, I believe you would need to urlencode those. Then, in the snippet of code above, you would pass `url.getUserInfo()` thorugh `URLDecoder.decode()` first (@Peter Rader). – m01 Apr 20 '17 at 21:14
  • Thanks, but for android you should use Base64 as following: String basicAuth = "Basic " + Base64.encodeToString(url.getUserInfo().getBytes(), Base64.DEFAULT); httpConn.setRequestProperty("Authorization", basicAuth); – valerybodak May 18 '20 at 09:53
  • Thank you valerybodak for that complement! – javabeangrinder May 18 '20 at 12:26
8

As i have came here looking for an Android-Java-Answer i am going to do a short summary:

  1. Use java.net.Authenticator as shown by James van Huis
  2. Use Apache Commons HTTP Client, as in this Answer
  3. Use basic java.net.URLConnection and set the Authentication-Header manually like shown here

If you want to use java.net.URLConnection with Basic Authentication in Android try this code:

URL url = new URL("http://www.mywebsite.com/resource");
URLConnection urlConnection = url.openConnection();
String header = "Basic " + new String(android.util.Base64.encode("user:pass".getBytes(), android.util.Base64.NO_WRAP));
urlConnection.addRequestProperty("Authorization", header);
// go on setting more request headers, reading the response, etc
Community
  • 1
  • 1
DaniEll
  • 970
  • 4
  • 21
  • 28
6

Was able to set the auth using the HttpsURLConnection

           URL myUrl = new URL(httpsURL);
            HttpsURLConnection conn = (HttpsURLConnection)myUrl.openConnection();
            String userpass = username + ":" + password;
            String basicAuth = "Basic " + new String(Base64.getEncoder().encode(userpass.getBytes()));
            //httpsurlconnection
            conn.setRequestProperty("Authorization", basicAuth);

few of the changes fetched from this post. and Base64 is from java.util package.

Tim
  • 641
  • 1
  • 7
  • 18
4

Be really careful with the "Base64().encode()"approach, my team and I got 400 Apache bad request issues because it adds a \r\n at the end of the string generated.

We found it sniffing packets thanks to Wireshark.

Here is our solution :

import org.apache.commons.codec.binary.Base64;

HttpGet getRequest = new HttpGet(endpoint);
getRequest.addHeader("Authorization", "Basic " + getBasicAuthenticationEncoding());

private String getBasicAuthenticationEncoding() {

        String userPassword = username + ":" + password;
        return new String(Base64.encodeBase64(userPassword.getBytes()));
    }

Hope it helps!

lboix
  • 919
  • 12
  • 13
3

Use this code for basic authentication.

URL url = new URL(path);
String userPass = "username:password";
String basicAuth = "Basic " + Base64.encodeToString(userPass.getBytes(), Base64.DEFAULT);//or
//String basicAuth = "Basic " + new String(Base64.encode(userPass.getBytes(), Base64.No_WRAP));
HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.setRequestProperty("Authorization", basicAuth);
urlConnection.connect();
Sarith Nob
  • 43
  • 8
3

I'd like to provide an answer for the case that you do not have control over the code that opens the connection. Like I did when using the URLClassLoader to load a jar file from a password protected server.

The Authenticator solution would work but has the drawback that it first tries to reach the server without a password and only after the server asks for a password provides one. That's an unnecessary roundtrip if you already know the server would need a password.

public class MyStreamHandlerFactory implements URLStreamHandlerFactory {

    private final ServerInfo serverInfo;

    public MyStreamHandlerFactory(ServerInfo serverInfo) {
        this.serverInfo = serverInfo;
    }

    @Override
    public URLStreamHandler createURLStreamHandler(String protocol) {
        switch (protocol) {
            case "my":
                return new MyStreamHandler(serverInfo);
            default:
                return null;
        }
    }

}

public class MyStreamHandler extends URLStreamHandler {

    private final String encodedCredentials;

    public MyStreamHandler(ServerInfo serverInfo) {
        String strCredentials = serverInfo.getUsername() + ":" + serverInfo.getPassword();
        this.encodedCredentials = Base64.getEncoder().encodeToString(strCredentials.getBytes());
    }

    @Override
    protected URLConnection openConnection(URL url) throws IOException {
        String authority = url.getAuthority();
        String protocol = "http";
        URL directUrl = new URL(protocol, url.getHost(), url.getPort(), url.getFile());

        HttpURLConnection connection = (HttpURLConnection) directUrl.openConnection();
        connection.setRequestProperty("Authorization", "Basic " + encodedCredentials);

        return connection;
    }

}

This registers a new protocol my that is replaced by http when credentials are added. So when creating the new URLClassLoader just replace http with my and everything is fine. I know URLClassLoader provides a constructor that takes an URLStreamHandlerFactory but this factory is not used if the URL points to a jar file.

FLUXparticle
  • 345
  • 3
  • 8
2

Since Java 9, you can do this

URL url = new URL("http://www.example.com");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setAuthenticator(new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication ("USER", "PASS".toCharArray());
    }
});
Punyapat
  • 319
  • 3
  • 6
0

ANDROD IMPLEMENTATION A complete method to request data/string response from web service requesting authorization with username and password

public static String getData(String uri, String userName, String userPassword) {
        BufferedReader reader = null;
        byte[] loginBytes = (userName + ":" + userPassword).getBytes();

        StringBuilder loginBuilder = new StringBuilder()
                .append("Basic ")
                .append(Base64.encodeToString(loginBytes, Base64.DEFAULT));

        try {
            URL url = new URL(uri);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.addRequestProperty("Authorization", loginBuilder.toString());

            StringBuilder sb = new StringBuilder();
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = reader.readLine())!= null){
                sb.append(line);
                sb.append("\n");
            }

            return  sb.toString();

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (null != reader){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
Emmanuel Mtali
  • 2,242
  • 17
  • 38
0

i did that this way you need to do this just copy paste it be happy

    HttpURLConnection urlConnection;
    String url;
 //   String data = json;
    String result = null;
    try {
        String username ="danish.hussain@gmail.com";
        String password = "12345678";

        String auth =new String(username + ":" + password);
        byte[] data1 = auth.getBytes(UTF_8);
        String base64 = Base64.encodeToString(data1, Base64.NO_WRAP);
        //Connect
        urlConnection = (HttpURLConnection) ((new URL(urlBasePath).openConnection()));
        urlConnection.setDoOutput(true);
        urlConnection.setRequestProperty("Content-Type", "application/json");
        urlConnection.setRequestProperty("Authorization", "Basic "+base64);
        urlConnection.setRequestProperty("Accept", "application/json");
        urlConnection.setRequestMethod("POST");
        urlConnection.setConnectTimeout(10000);
        urlConnection.connect();
        JSONObject obj = new JSONObject();

        obj.put("MobileNumber", "+97333746934");
        obj.put("EmailAddress", "danish.hussain@mee.com");
        obj.put("FirstName", "Danish");
        obj.put("LastName", "Hussain");
        obj.put("Country", "BH");
        obj.put("Language", "EN");
        String data = obj.toString();
        //Write
        OutputStream outputStream = urlConnection.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
        writer.write(data);
        writer.close();
        outputStream.close();
        int responseCode=urlConnection.getResponseCode();
        if (responseCode == HttpsURLConnection.HTTP_OK) {
            //Read
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));

        String line = null;
        StringBuilder sb = new StringBuilder();

        while ((line = bufferedReader.readLine()) != null) {
            sb.append(line);
        }

        bufferedReader.close();
        result = sb.toString();

        }else {
        //    return new String("false : "+responseCode);
        new String("false : "+responseCode);
        }

    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (JSONException e) {
        e.printStackTrace();
    }
Syed Danish Haider
  • 1,126
  • 9
  • 14