Java中使用HttpClient进行Http Basic身份验证?

179

我正在尝试在Java中模仿这个curl命令的功能:

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

我使用 Commons HttpClient 3.0 写了以下代码,但是从服务器返回了 500 Internal Server Error。请问我做错了什么吗?

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

后来我尝试了Commons HttpClient 4.0.1,但仍然出现相同的错误:

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

嗯,服务器日志中显示了什么错误? - hvgotcodes
啊...我没有访问服务器日志的权限 :( - Legend
大多数情况下,我们使用的授权密钥可能是错误的。 请访问http://dev.tapjoy.com/faq/how-to-find-sender-id-and-api-key-for-gcm/检查您是否使用了正确的密钥。在选择Firebase的API密钥时,我也感到困惑。 我们必须在Firebase设置的Cloud Messaging选项卡中使用SENDER ID - API KEY对。 即 进入Firebase应用程序->进入应用程序设置-> Cloud Messaging 在那里,您可以找到Sender Id <==> API key 您可以使用此API密钥发送FCM。 - Rahul
10个回答

227

你尝试过这样做了吗(使用HttpClient版本4):

String encoding = Base64.getEncoder().encodeToString((user + ":" + pwd).getBytes());
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();

79
建议使用Java 8中的java.util.Base64: Base64.getEncoder().encodeToString(("test1:test1").getBytes()); - Michael Berry
1
我更喜欢使用javax.xml.bind.DatatypeConverter来进行base64、hex和其他转换。它是jdk的一部分,因此无需包含任何额外的JAR。 - Mubashar
1
这是适用于我的使用情况的版本,其中HttpClient已经提供,而您无法在构建httpclient时设置setDefaultCredentialsProvider()的生成器。此外,我喜欢它,因为它是每次调用范围内的,而不是在整个httpclient范围内。 - Tony

128

好的,这个可以用。以防有人需要,这是对我有效的版本 :)

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
找不到Base64Encoder。 Jonas,可以给我完整的jar包吗?另外,“Base64Encoder”的完全限定类名是什么? - Jus12
@Amitabh: Base64Encoder请看这里Base64在commons-codec-1.6.jar中,在Apache HttpComponents Downloads 的4.2.5.zip包中,查看文档,导入import org.apache.commons.codec.binary.Base64; - Lernkurve
29
这并没有回答问题。问题是关于使用 HttpClient,但是这个回答并没有使用 HttpClient。 - Paul Croarkin
9
如果您正在使用Java 8,您可以使用java.util.Base64。 - WW.
4
这是Java中的java.util.Base64库的代码行:String encoding = Base64.getEncoder().encodeToString("test1:test1".getBytes("utf-8"));它的作用是将字符串"test1:test1"使用Base64编码转换为一个新的字符串。 - Joe
显示剩余7条评论

19

这是来自上面被接受的回答的代码,对于Base64编码进行了一些更改。以下代码可以编译。

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

一个小更新 - 希望对某些人有用 - 在我的项目中它对我有效:

  • 我使用了来自Robert Harder的优秀公共领域类Base64.java(感谢Robert - 代码可在此处获得:Base64 - 下载并将其放入您的包中)。

  • 并使用身份验证下载文件(图像,文档等)并写入本地磁盘

示例:

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
我得到了“类型Base64未定义方法encodeBytes(byte[])”的错误。 - Francisco Corrales Morales
可以使用import org.apache.commons.codec.binary.Base64;替换自定义的Base64类,详见此页面上的此答案 - Brad Parks
3
在Java 8中,您可以使用:import java.util.Base64; - WW.

7
使用头部数组时
String auth = Base64.getEncoder().encodeToString(("test1:test1").getBytes());
Header[] headers = {
    new BasicHeader(HTTP.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()),
    new BasicHeader("Authorization", "Basic " +auth)
};

7
以下是几个要点:
  • You could consider upgrading to HttpClient 4 (generally speaking, if you can, I don't think version 3 is still actively supported).

  • A 500 status code is a server error, so it might be useful to see what the server says (any clue in the response body you're printing?). Although it might be caused by your client, the server shouldn't fail this way (a 4xx error code would be more appropriate if the request is incorrect).

  • I think setDoAuthentication(true) is the default (not sure). What could be useful to try is pre-emptive authentication works better:

    client.getParams().setAuthenticationPreemptive(true);
    
否则,curl -d "" 和你在Java中所做的主要区别是,除了 Content-Length: 0,curl 还会发送 Content-Type: application/x-www-form-urlencoded。请注意,在设计方面,您应该无论如何都发送一个实体作为您的 POST 请求。

5
使用HTTPClient BasicCredentialsProvider 是一种不需要进行任何Base64特定调用的HTTP POST登录的简单方法。
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);

这对我不起作用。调用可以正常工作,但没有身份验证头。 - lukas84
你的提供者设置正确吗? - rjdkolb
也尝试更新您的库版本。这对我有用。 - rjdkolb
1
对于基本身份验证文件下载,这对我很有效,但我使用的是HttpGet而不是HttpPost。谢谢。 - Taz

5

感谢上面所有的回答,但我找不到Base64Encoder类,所以我还是自己想了办法。

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

}

还有一件事,我也尝试了

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

由于它返回一个与

几乎相同的字符串,因此它无法正常工作。
DatatypeConverter.printBase64Binary()

如果以"\r\n"结尾,服务器将返回"bad request"。

另外,以下代码也可以正常工作,实际上我首先整理了这个代码,但由于某些原因,在某些云环境(如果您想知道,它是一个中国的云服务:sae.sina.com.cn)中无法正常工作,所以必须使用HTTP头而不是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("user:passwd".getBytes()); 对我有效。DatatypeConverter.printBase64Binary() 也对我有效。可能是您在之前的消息正文中犯了错误,导致了错误请求。或者可能取决于服务器。 - rents

3

对我来说,HttpBasicAuth在做一些小的更改后可以正常工作。

  1. I use maven dependency

    <dependency>
        <groupId>net.iharder</groupId>
        <artifactId>base64</artifactId>
        <version>2.3.8</version>
    </dependency>
    
  2. Smaller change

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

3

对于HttpClient,始终使用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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接