¿Autenticación básica HTTP en Java usando HttpClient?

156

Estoy tratando de imitar la funcionalidad de este comando curl en Java:

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

Escribí lo siguiente usando Commons HttpClient 3.0 pero de alguna manera terminé recibiendo un 500 Internal Server Errormensaje del servidor. ¿Alguien puede decirme si estoy haciendo algo mal?

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

Y luego probé un HttpClient 4.0.1 de Commons pero aún el mismo error:

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();
        }
}
Leyenda
fuente
¿Cuál es el error que aparece en los registros del servidor?
hvgotcodes
Ah ... No tengo acceso a los registros del servidor :(
Legend
La mayoría de las veces la clave de autorización que estamos usando podría estar equivocada. Verifique dev.tapjoy.com/faq/how-to-find-sender-id-and-api-key-for-gcm para ver si está utilizando la clave correcta o no. También me confundí al seleccionar la clave API para firebase. Tenemos que usar ID SENDER - par CLAVE API en la pestaña de mensajes en la nube en la configuración de firebase. es decir, vaya a la aplicación firebase -> Vaya a la configuración de la aplicación -> Cloud Messaging, allí puede encontrar Id. del remitente <==> clave de API y esta clave de API que puede usar para enviar FCM.
Rahul

Respuestas:

187

¿Has probado esto (usando HttpClient versión 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
fuente
64
Mejor usar a java.util.Base64partir de Java 8:Base64.getEncoder().encodeToString(("test1:test1").getBytes());
Michael Berry
1
Prefiero usar javax.xml.bind.DatatypeConverter para convertir a base64, hexadecimal y otras conversiones. es parte de jdk, por lo que no es necesario incluir ningún JAR adicional.
Mubashar
1
Esta es la versión que funciona para mí en mi caso de uso donde el HttpClient ya está provisto y no puede establecer setDefaultCredentialsProvider () en el constructor mientras construye el httpclient. También me gusta porque es por alcance de llamada. No en todo el ámbito httpclient.
Tony
114

Ok, entonces este funciona. Por si alguien lo quiere, aquí está la versión que funciona para mí :)

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

    }

}
Leyenda
fuente
44
No se puede encontrar Base64Encoder. Jonas, ¿puedes dar el frasco completo? ¿Cuál es el nombre completo de la clase de Base64Encoder?
Jus12
@Amitabh: Para Base64Encoderver aquí . Para Base64buscar en commons-codec-1.6.jar en 4.2.5.zip en Apache HttpComponents Downloads , doc ,import org.apache.commons.codec.binary.Base64;
Lernkurve
22
Esto no responde la pregunta. La pregunta se refiere al uso de HttpClient y esta respuesta no usa HttpClient.
Paul Croarkin
9
Si está utilizando Java 8, puede usar java.util.Base64.
WW.
44
Aquí está la línea para java.util.Base64String encoding = Base64.getEncoder().encodeToString("test1:test1".getBytes("utf-8"));
Joe
16

Este es el código de la respuesta aceptada anteriormente, con algunos cambios realizados con respecto a la codificación Base64. El siguiente código se compila.

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
fuente
14

Una pequeña actualización, con suerte útil para alguien, funciona para mí en mi proyecto:

  • Uso la buena clase de dominio público Base64.java de Robert Harder (Gracias Robert - Código disponible aquí: Base64 - descárgalo y ponlo en tu paquete).

  • y descargue un archivo (imagen, documento, etc.) con autenticación y escriba en el disco local

Ejemplo:

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
fuente
2
ObtengoThe method encodeBytes(byte[]) is undefined for the type Base64
Francisco Corrales Morales
La clase Base64 personalizada se puede reemplazar import org.apache.commons.codec.binary.Base64; como se detalla en esta respuesta en esta página
Brad Parks
3
En java 8, puede usar: import java.util.Base64;
WW.
7

Aquí hay algunos puntos:

  • Podría considerar actualizar a HttpClient 4 (en términos generales, si puede, no creo que la versión 3 siga siendo compatible).

  • Un código de estado 500 es un error del servidor, por lo que puede ser útil ver lo que dice el servidor (¿alguna pista en el cuerpo de respuesta que está imprimiendo?). Aunque puede ser causado por su cliente, el servidor no debería fallar de esta manera (un código de error 4xx sería más apropiado si la solicitud es incorrecta).

  • Creo que setDoAuthentication(true)es el valor predeterminado (no estoy seguro). Lo que podría ser útil para probar es que la autenticación preventiva funciona mejor:

    client.getParams().setAuthenticationPreemptive(true);

De lo contrario, la principal diferencia entre curl -d ""y lo que está haciendo en Java es que, además de Content-Length: 0, curl también envía Content-Type: application/x-www-form-urlencoded. Tenga en cuenta que, en términos de diseño, probablemente debería enviar una entidad con su POSTsolicitud de todos modos.

Bruno
fuente
5

Gracias por todas las respuestas anteriores, pero para mí, no puedo encontrar la clase Base64Encoder, así que lo soluciono de todos modos.

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

}

Una cosa más, también intenté

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

NO funciona debido a que devuelve una cadena casi igual con

DatatypeConverter.printBase64Binary()

pero termina con "\ r \ n", entonces el servidor devolverá "solicitud incorrecta".

Además, el siguiente código también funciona, en realidad lo soluciono primero, pero por alguna razón, NO funciona en algún entorno de nube (sae.sina.com.cn si quieres saberlo, es un servicio de nube chino). así que tengo que usar el encabezado http en lugar de las credenciales 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
fuente
Base64.encodeBase64String ("usuario: passwd" .getBytes ()); trabajó para mi. DatatypeConverter.printBase64Binary () también funcionó para mí. Es posible que haya cometido un error en el cuerpo del mensaje en un caso anterior y que haya causado una mala solicitud. O tal vez depende del servidor.
alquila
5

mientras usa la matriz de encabezado

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
fuente
3

para HttpClient siempre use HttpRequestInterceptor por ejemplo

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
fuente
3

HttpBasicAuth funciona para mí con pequeños cambios

  1. Yo uso dependencia maven

    <dependency>
        <groupId>net.iharder</groupId>
        <artifactId>base64</artifactId>
        <version>2.3.8</version>
    </dependency>
  2. Cambio más pequeño

    String encoding = Base64.encodeBytes ((user + ":" + passwd).getBytes());
lynhnn
fuente
1

Una manera fácil de iniciar sesión con un HTTP POST sin hacer ninguna llamada específica de Base64 es usar 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
fuente
Esto no está funcionando para mí. La llamada funciona pero no hay un encabezado de autenticación presente.
lukas84
Extraño, ¿su proveedor está configurado correctamente?
rjdkolb
También intente actualizar la versión de su biblioteca. Esto funcionó para mí
rjdkolb