使用 HttpClient 在 Java 中进行 Http 基本身份验证?

Posted

技术标签:

【中文标题】使用 HttpClient 在 Java 中进行 Http 基本身份验证?【英文标题】:Http Basic Authentication in Java using HttpClient? 【发布时间】:2011-03-18 00:41:41 【问题描述】:

我正在尝试在 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();
        

【问题讨论】:

嗯,服务器日志中显示的错误是什么? 啊...我无权访问服务器日志 :( 大多数时候我们使用的授权密钥可能是错误的。检查dev.tapjoy.com/faq/how-to-find-sender-id-and-api-key-for-gcm 以查看您是否使用了正确的密钥。我在为 firebase 选择 API 密钥时也感到困惑我们必须在 firebase 设置下的 Cloud 消息选项卡中使用 SENDER ID - API KEY 对。即转到 firebase App--> Go to app setting --> Cloud Messaging 在那里你可以找到 Sender Id API key 和这个 API key 你可以用来发送 FCM。 【参考方案1】:

您是否尝试过(使用 HttpClient 版本 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();

【讨论】:

从 Java 8 开始最好使用 java.util.Base64Base64.getEncoder().encodeToString(("test1:test1").getBytes()); 我更喜欢使用 javax.xml.bind.DatatypeConverter 从到 base64、hex 和其他转换。它是 jdk 的一部分,因此无需包含任何额外的 JAR。 这是适用于我的用例的版本,其中已经提供了 HttpClient,并且在构建 httpclient 时您无法在构建器上设置 setDefaultCredentialsProvider()。我也喜欢它,因为它是每个调用范围。不在整个 httpclient 范围内。【参考方案2】:

使用 Header 数组时

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】:

使用 HTTP POST 不执行任何 Base64 特定调用简单方法是使用 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);

【讨论】:

这对我不起作用。调用有效,但不存在身份验证标头。 奇怪,你的provider设置正确了吗? 也尝试更新你的库的版本。这对我有用 对于基本身份验证文件下载它对我有用,但我使用 HttpGet 而不是 HttpPost。谢谢。【参考方案4】:

好的,所以这个工作。以防万一有人想要它,这是适合我的版本:)

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

    


【讨论】:

找不到Base64Encoder。乔纳斯你能把完整的罐子给吗?还有Base64Encoder 的完全限定类名是什么? @Amitabh:对于Base64Encoder,请查看here。对于Base64,请查看 4.2.5.zip 中的 commons-codec-1.6.jar,地址为 Apache HttpComponents Downloads、doc、import org.apache.commons.codec.binary.Base64; 这没有回答问题。该问题询问有关使用 HttpClient 的问题,而此答案不使用 HttpClient。 如果您使用的是 Java 8,则可以使用 java.util.Base64。 这是 java.util.Base64 String encoding = Base64.getEncoder().encodeToString("test1:test1".getBytes("utf-8")); 的行【参考方案5】:

这是上面接受的答案中的代码,对 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();
        
    

【讨论】:

【参考方案6】:

HttpBasicAuth 对我来说只需较小的更改

    我使用maven依赖

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

    较小的变化

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

【讨论】:

【参考方案7】:

感谢以上所有答案,但对我来说,我找不到 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”结尾,则服务器将返回“错误请求”。

下面的代码也可以,其实我先整理一下,但是由于某种原因,它在某些云环境中不起作用(如果你想知道,它是中国云,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() 也为我工作。可能是您在较早的情况下在消息正文中犯了错误,从而导致了错误的请求。或者可能取决于服务器。【参考方案8】:

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

【讨论】:

【参考方案9】:

一个小更新 - 希望对某人有用 - 它适用于我的项目:

我使用来自 Robert Harder 的漂亮的 Public Domain 类 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();
    


【讨论】:

我收到The method encodeBytes(byte[]) is undefined for the type Base64 自定义Base64类可以用import org.apache.commons.codec.binary.Base64; 替换成detailed in this answer on this page 在java 8中,可以使用:import java.util.Base64;【参考方案10】:

这里有几点:

您可以考虑升级到 HttpClient 4(一般来说,如果可以的话,我认为版本 3 仍然不被积极支持)。

500 状态代码是服务器错误,因此查看服务器所说的内容可能很有用(您正在打印的响应正文中有任何线索吗?)。虽然它可能是由您的客户端引起的,但服务器不应该以这种方式失败(如果请求不正确,4xx 错误代码会更合适)。

我认为setDoAuthentication(true) 是默认值(不确定)。可能有用的是先发制人的身份验证效果更好:

client.getParams().setAuthenticationPreemptive(true);

否则,curl -d "" 与您在 Java 中所做的主要区别在于,除了Content-Length: 0,curl 还发送Content-Type: application/x-www-form-urlencoded。请注意,在设计方面,无论如何您都应该发送带有POST 请求的实体。

【讨论】:

以上是关于使用 HttpClient 在 Java 中进行 Http 基本身份验证?的主要内容,如果未能解决你的问题,请参考以下文章

HttpClient--使用HttpClient进行Get Post请求访问

尝试在 Java 中使用 HttpClient 发送 HTTP Post GraphQL 查询时出现 HTTP 400

如何在 Jetty HttpClient 中进行多部分/表单数据发布

Java使用HttpClient实现Post请求

JAVA利用HttpClient进行POST请求(HTTPS)

Java使用HttpClient实现Post请求