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();
        }
}

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();

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();
        }

    }

}

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();
        }
    }
}

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();
    }
}
}

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.


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();
    }
}

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)
};

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);

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());

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);

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.