Podstawowe uwierzytelnianie HTTP w Javie przy użyciu HttpClient?

156

Próbuję naśladować funkcjonalność tego polecenia curl w Javie:

curl --basic --user username:password -d "" http://ipaddress/test/login

Napisałem następujące rzeczy przy użyciu Commons HttpClient 3.0, ale w jakiś sposób otrzymałem 500 Internal Server Errorz serwera. Czy ktoś może mi powiedzieć, czy robię coś złego?

public class HttpBasicAuth {

    private static final String ENCODING = "UTF-8";

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {

            HttpClient client = new HttpClient();

            client.getState().setCredentials(
                    new AuthScope("ipaddress", 443, "realm"),
                    new UsernamePasswordCredentials("test1", "test1")
                    );

            PostMethod post = new PostMethod(
                    "http://address/test/login");

            post.setDoAuthentication( true );

            try {
                int status = client.executeMethod( post );
                System.out.println(status + "\n" + post.getResponseBodyAsString());
            } finally {
                // release any connection resources used by the method
                post.releaseConnection();
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
   } 

Później wypróbowałem Commons HttpClient 4.0.1, ale nadal ten sam błąd:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;


public class HttpBasicAuth {

    private static final String ENCODING = "UTF-8";

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        try {
            DefaultHttpClient httpclient = new DefaultHttpClient();

            httpclient.getCredentialsProvider().setCredentials(
                    new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), 
                    new UsernamePasswordCredentials("test1", "test1"));

            HttpPost httppost = new HttpPost("http://host:post/test/login");

            System.out.println("executing request " + httppost.getRequestLine());
            HttpResponse response;
            response = httpclient.execute(httppost);
            HttpEntity entity = response.getEntity();

            System.out.println("----------------------------------------");
            System.out.println(response.getStatusLine());
            if (entity != null) {
                System.out.println("Response content length: " + entity.getContentLength());
            }
            if (entity != null) {
                entity.consumeContent();
            }

            httpclient.getConnectionManager().shutdown();  
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
}
Legenda
źródło
um, co to za błąd w logach serwera?
hvgotcodes
Ach ... Nie mam dostępu do logów serwera :(
Legenda,
W większości przypadków używany przez nas klucz autoryzacji może być nieprawidłowy. Sprawdź dev.tapjoy.com/faq/how-to-find-sender-id-and-api-key-for-gcm, aby sprawdzić, czy używasz właściwego klucza, czy nie. Pomyliłem się też wybierając klucz API do firebase. Musimy użyć pary SENDER ID - API KEY w zakładce Cloud Messaging w ustawieniach firebase. tj. Idź do aplikacji Firebase -> Idź do ustawień aplikacji -> Komunikacja w chmurze tam znajdziesz Sender Id <==> klucz API i ten klucz API, którego możesz użyć do wysłania FCM.
Rahul

Odpowiedzi:

187

Czy próbowałeś tego (używając HttpClient w wersji 4):

String encoding = Base64Encoder.encode(user + ":" + pwd);
HttpPost httpPost = new HttpPost("http://host:post/test/login");
httpPost.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + encoding);

System.out.println("executing request " + httpPost.getRequestLine());
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
Buhake Sindi
źródło
64
Lepiej używać java.util.Base64od wersji Java 8:Base64.getEncoder().encodeToString(("test1:test1").getBytes());
Michael Berry
1
Wolę używać javax.xml.bind.DatatypeConverter do konwersji z formatu base64, hex i inne. jest częścią jdk, więc nie ma potrzeby dołączania dodatkowego pliku JAR.
Mubashar
1
To jest wersja, która działa dla mnie w moim przypadku użycia, w którym HttpClient jest już dostarczony i nie można ustawić setDefaultCredentialsProvider () w konstruktorze podczas budowania httpclient. Podoba mi się również, ponieważ jest to zakres wywołania. Nie w całym zakresie httpclient.
Tony
114

Ok, więc ten działa. Na wypadek, gdyby ktoś tego chciał, oto wersja, która działa dla mnie :)

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;


public class HttpBasicAuth {

    public static void main(String[] args) {

        try {
            URL url = new URL ("http://ip:port/login");
            String encoding = Base64.getEncoder().encodeToString(("test1:test1").getBytes(‌"UTF‌​-8"​));

            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setRequestProperty  ("Authorization", "Basic " + encoding);
            InputStream content = (InputStream)connection.getInputStream();
            BufferedReader in   = 
                new BufferedReader (new InputStreamReader (content));
            String line;
            while ((line = in.readLine()) != null) {
                System.out.println(line);
            }
        } catch(Exception e) {
            e.printStackTrace();
        }

    }

}
Legenda
źródło
4
Nie można znaleźć Base64Encoder. Jonas, czy możesz podać pełny słoik? Jaka jest w pełni kwalifikowana nazwa klasy Base64Encoder?
Jus12
@Amitabh: Base64EncoderSpójrz tutaj . Na Base64wygląd w świetlicy-codec-1.6.jar w 4.2.5.zip w Apache HttpComponents pobrania , doc ,import org.apache.commons.codec.binary.Base64;
Lernkurve
22
To nie odpowiada na pytanie. Pytanie dotyczy korzystania z HttpClient, a ta odpowiedź nie używa HttpClient.
Paul Croarkin
9
Jeśli korzystasz z Java 8, możesz użyć java.util.Base64.
WW.
4
Oto wiersz dla java.util.Base64String encoding = Base64.getEncoder().encodeToString("test1:test1".getBytes("utf-8"));
Joe
16

To jest kod z zaakceptowanej odpowiedzi powyżej, z pewnymi zmianami dotyczącymi kodowania Base64. Poniższy kod jest kompilowany.

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

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


public class HttpBasicAuth {

    public static void main(String[] args) {

        try {
            URL url = new URL ("http://ip:port/login");

            Base64 b = new Base64();
            String encoding = b.encodeAsString(new String("test1:test1").getBytes());

            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setRequestProperty  ("Authorization", "Basic " + encoding);
            InputStream content = (InputStream)connection.getInputStream();
            BufferedReader in   = 
                new BufferedReader (new InputStreamReader (content));
            String line;
            while ((line = in.readLine()) != null) {
                System.out.println(line);
            }
        } 
        catch(Exception e) {
            e.printStackTrace();
        }
    }
}
Mario
źródło
14

Mała aktualizacja - mam nadzieję, że komuś przyda się - działa na mnie w moim projekcie:

  • Używam ładnej klasy Public Domain Base64.java od Roberta Hardera (Dzięki Robert - kod dostępny tutaj: Base64 - pobierz i umieść w swoim pakiecie).

  • i pobierz plik (obraz, dokument, itp.) z uwierzytelnieniem i zapisz na dysku lokalnym

Przykład:

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpBasicAuth {

public static void downloadFileWithAuth(String urlStr, String user, String pass, String outFilePath) {
    try {
        // URL url = new URL ("http://ip:port/download_url");
        URL url = new URL(urlStr);
        String authStr = user + ":" + pass;
        String authEncoded = Base64.encodeBytes(authStr.getBytes());

        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.setDoOutput(true);
        connection.setRequestProperty("Authorization", "Basic " + authEncoded);

        File file = new File(outFilePath);
        InputStream in = (InputStream) connection.getInputStream();
        OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
        for (int b; (b = in.read()) != -1;) {
            out.write(b);
        }
        out.close();
        in.close();
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}
}
Ralph At skillyard
źródło
2
DostajęThe method encodeBytes(byte[]) is undefined for the type Base64
Francisco Corrales Morales
Niestandardową klasę Base64 można zastąpić import org.apache.commons.codec.binary.Base64; zgodnie z opisem w tej odpowiedzi na tej stronie
Brad Parks,
3
W java 8 możesz użyć: import java.util.Base64;
WW.
7

Oto kilka punktów:

  • Możesz rozważyć aktualizację do HttpClient 4 (ogólnie mówiąc, jeśli możesz, nie sądzę, aby wersja 3 była nadal aktywnie obsługiwana).

  • Kod statusu 500 to błąd serwera, więc warto zobaczyć, co mówi serwer (czy jest jakaś wskazówka w treści odpowiedzi, którą drukujesz?). Chociaż może to być spowodowane przez twojego klienta, serwer nie powinien zawieść w ten sposób (kod błędu 4xx byłby bardziej odpowiedni, gdyby żądanie było niepoprawne).

  • Myślę, że setDoAuthentication(true)jest to ustawienie domyślne (nie jestem pewien). Warto wypróbować uwierzytelnianie z wywłaszczaniem, które działa lepiej:

    client.getParams().setAuthenticationPreemptive(true);

W przeciwnym razie główna różnica między curl -d ""tym, co robisz w Javie, polega na tym, że oprócz tego Content-Length: 0, curl wysyła Content-Type: application/x-www-form-urlencoded. Zwróć uwagę, że jeśli chodzi o projekt, i tak powinieneś prawdopodobnie wysłać podmiot ze swoim POSTżądaniem.

Bruno
źródło
5

Dzięki za wszystkie powyższe odpowiedzi, ale dla mnie nie mogę znaleźć klasy Base64Encoder, więc i tak sobie radzę.

public static void main(String[] args) {
    try {
        DefaultHttpClient Client = new DefaultHttpClient();

        HttpGet httpGet = new HttpGet("https://httpbin.org/basic-auth/user/passwd");
        String encoding = DatatypeConverter.printBase64Binary("user:passwd".getBytes("UTF-8"));
        httpGet.setHeader("Authorization", "Basic " + encoding);

        HttpResponse response = Client.execute(httpGet);

        System.out.println("response = " + response);

        BufferedReader breader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
        StringBuilder responseString = new StringBuilder();
        String line = "";
        while ((line = breader.readLine()) != null) {
            responseString.append(line);
        }
        breader.close();
        String repsonseStr = responseString.toString();

        System.out.println("repsonseStr = " + repsonseStr);

    } catch (IOException e) {
        e.printStackTrace();
    }

}

Jeszcze jedno, też próbowałem

Base64.encodeBase64String("user:passwd".getBytes());

NIE działa, ponieważ zwraca ciąg prawie taki sam z

DatatypeConverter.printBase64Binary()

ale kończy się na „\ r \ n”, serwer zwróci „złe żądanie”.

Działa również poniższy kod, właściwie najpierw go rozwiązuję, ale z jakiegoś powodu NIE działa w niektórych środowiskach chmurowych (sae.sina.com.cn, jeśli chcesz wiedzieć, jest to chińska usługa w chmurze). więc musisz użyć nagłówka http zamiast poświadczeń HttpClient.

public static void main(String[] args) {
    try {
        DefaultHttpClient Client = new DefaultHttpClient();
        Client.getCredentialsProvider().setCredentials(
                AuthScope.ANY,
                new UsernamePasswordCredentials("user", "passwd")
        );

        HttpGet httpGet = new HttpGet("https://httpbin.org/basic-auth/user/passwd");
        HttpResponse response = Client.execute(httpGet);

        System.out.println("response = " + response);

        BufferedReader breader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
        StringBuilder responseString = new StringBuilder();
        String line = "";
        while ((line = breader.readLine()) != null) {
            responseString.append(line);
        }
        breader.close();
        String responseStr = responseString.toString();
        System.out.println("responseStr = " + responseStr);

    } catch (IOException e) {
        e.printStackTrace();
    }
}
chenyi1976
źródło
Base64.encodeBase64String ("użytkownik: passwd" .getBytes ()); pracował dla mnie. DatatypeConverter.printBase64Binary () również działał dla mnie. Możliwe, że we wcześniejszym przypadku popełniłeś błąd w treści wiadomości, co spowodowało błędne żądanie. A może zależy to od serwera.
wypożycza
5

podczas korzystania z tablicy nagłówków

String auth = Base64.getEncoder().encodeToString(("test1:test1").getBytes());
Header[] headers = {
    new BasicHeader(HTTP.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()),
    new BasicHeader("Authorization", "Basic " +auth)
};
manoj
źródło
3

dla HttpClient zawsze używaj na przykład HttpRequestInterceptor

httclient.addRequestInterceptor(new HttpRequestInterceptor() {
    public void process(HttpRequest arg0, HttpContext context) throws HttpException, IOException {
        AuthState state = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
        if (state.getAuthScheme() == null) {
            BasicScheme scheme = new BasicScheme();
            CredentialsProvider credentialsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
            Credentials credentials = credentialsProvider.getCredentials(AuthScope.ANY);
            if (credentials == null) {
                System.out.println("Credential >>" + credentials);
                throw new HttpException();
            }
            state.setAuthScope(AuthScope.ANY);
            state.setAuthScheme(scheme);
            state.setCredentials(credentials);
        }
    }
}, 0);
EngrSofty
źródło
3

HttpBasicAuth działa dla mnie z mniejszymi zmianami

  1. Używam zależności Mavena

    <dependency>
        <groupId>net.iharder</groupId>
        <artifactId>base64</artifactId>
        <version>2.3.8</version>
    </dependency>
    
  2. Mniejsza zmiana

    String encoding = Base64.encodeBytes ((user + ":" + passwd).getBytes());
lynhnn
źródło
1

Łatwym sposobem logowania się za pomocą HTTP POST bez wykonywania jakichkolwiek wywołań związanych z Base64 jest użycie HTTPClient BasicCredentialsProvider

import java.io.IOException;
import static java.lang.System.out;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;

//code
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user, password);
provider.setCredentials(AuthScope.ANY, credentials);
HttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build();

HttpResponse response = client.execute(new HttpPost("http://address/test/login"));//Replace HttpPost with HttpGet if you need to perform a GET to login
int statusCode = response.getStatusLine().getStatusCode();
out.println("Response Code :"+ statusCode);
rjdkolb
źródło
To nie działa dla mnie. Wywołanie działa, ale nie ma nagłówka uwierzytelniania.
lukas84
Dziwne, czy twój dostawca jest odpowiednio ustawiony?
rjdkolb
Spróbuj także zaktualizować wersję swojej biblioteki. To zadziałało dla mnie
rjdkolb