Kết nối với URL từ xa yêu cầu xác thực bằng Java


125

Làm cách nào để kết nối với một URL từ xa trong Java yêu cầu xác thực. Tôi đang cố gắng tìm cách sửa đổi mã sau đây để có thể cung cấp tên người dùng / mật khẩu theo chương trình để nó không ném mã 401.

URL url = new URL(String.format("http://%s/manager/list", _host + ":8080"));
HttpURLConnection connection = (HttpURLConnection)url.openConnection();

Câu trả lời:


134

Bạn có thể đặt trình xác thực mặc định cho các yêu cầu http như thế này:

Authenticator.setDefault (new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication ("username", "password".toCharArray());
    }
});

Ngoài ra, nếu bạn yêu cầu linh hoạt hơn, bạn có thể kiểm tra Apache HTTPClient , sẽ cung cấp cho bạn nhiều tùy chọn xác thực hơn (cũng như hỗ trợ phiên, v.v.)


4
Làm thế nào để bạn xử lý một sự kiện xác thực xấu? [Ví dụ: nếu người dùng cung cấp thông tin xác thực tên người dùng và mật khẩu không khớp với bất cứ điều gì]?
SK9

2
Đoạn mã trên hoạt động nhưng khá tiềm ẩn những gì đang diễn ra. Có phân lớp và phương thức ghi đè đang diễn ra ở đó, đào sâu vào các tài liệu cho các lớp đó nếu bạn quan tâm để biết chuyện gì đang xảy ra. Mã ở đây rõ ràng hơn javacodegeek
Fuchida

12
Có thể đặt cụ thể Authenticatorcho mỗi lần URL.openConnection()gọi cụ thể thay vì đặt toàn cầu không?
Yuriy Nakonechnyy

@Yura: không. Nó phải là toàn cầu. Tuy nhiên, bạn có thể thực hiện những việc xấu như đặt trình xác thực toàn cầu để lấy thông tin đăng nhập khỏi các biến cục bộ và đặt thông tin đăng nhập cho mỗi luồng trước khi thực hiện kết nối HTTP.
David đưa ra

4
Đáp lại @YuriyNakonechnyy ... nếu bạn đang sử dụng Java 9 trở lên thì bạn có thể gọi HttpURLConnection.setAuthenticator(). Thật không may trên Java 8 trở về trước, cá thể Authenticator là một biến toàn cục trên toàn JVM. Xem: docs.oracle.com/javase/9/docs/api/java/net/ khuyên
Neil Bartlett

133

Có một sự thay thế tự nhiên và ít xâm phạm hơn, chỉ hoạt động cho cuộc gọi của bạn.

URL url = new URL(“location address”);
URLConnection uc = url.openConnection();
String userpass = username + ":" + password;
String basicAuth = "Basic " + new String(Base64.getEncoder().encode(userpass.getBytes()));
uc.setRequestProperty ("Authorization", basicAuth);
InputStream in = uc.getInputStream();

5
Lớp Base64 có thể được cung cấp bởi Apache Commons Codec.
Matthew Buckett

Không làm việc cho tôi theo cách này ... Chỉ có cách mà @James Van Huis là tốt
Miguel Ribeiro

Đó là 'bản địa' trên Grails và nhiều khung công tác Java khác vì tất cả chúng đều sử dụng Apache Commons Libs.
Wanderson Santos

1
Không hoạt động nói chung trừ khi bạn có .trim()kết quả hoặc gọi một biến thể phương thức không tạo ra đầu ra chunked. javax.xml.bind.DatatypeConvertercó vẻ an toàn hơn
Jesse Glick

Tôi đã trích dẫn mã của bạn trong câu trả lời này Cảm ơn bạn
ghi chú-jj

77

Bạn cũng có thể sử dụng các mục sau, không yêu cầu sử dụng các gói bên ngoài:

URL url = new URL(“location address”);
URLConnection uc = url.openConnection();

String userpass = username + ":" + password;
String basicAuth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(userpass.getBytes());

uc.setRequestProperty ("Authorization", basicAuth);
InputStream in = uc.getInputStream();

10
Tôi đã tìm kiếm một bộ mã hóa Base64 trong các gói tiêu chuẩn java từ rất lâu rồi! Cảm ơn bạn
qwertzguy

Lưu ý rằng đối với Java9 +, bạn có thể phải --add-modules javax.xml.bindxóa gói khỏi đường dẫn lớp mặc định. Và trong Java11 + nó đã bị xóa hoàn toàn, bạn cần một phụ thuộc bên ngoài một lần nữa. Đó là sự tiến bộ!
Ed Randall

1
@EdRandall có java.util.Base64ngay bây giờ vì vậy đó là sự tiến bộ thực sự :)
Steven Schlansker

42

Nếu bạn đang sử dụng thông tin đăng nhập bình thường trong khi nhập tên người dùng và mật khẩu giữa giao thức và tên miền thì điều này đơn giản hơn. Nó cũng hoạt động với và không có đăng nhập.

Mẫu Url: http: // user: pass@domain.com/url

URL url = new URL("http://user:pass@domain.com/url");
URLConnection urlConnection = url.openConnection();

if (url.getUserInfo() != null) {
    String basicAuth = "Basic " + new String(new Base64().encode(url.getUserInfo().getBytes()));
    urlConnection.setRequestProperty("Authorization", basicAuth);
}

InputStream inputStream = urlConnection.getInputStream();

Xin lưu ý trong nhận xét, từ valerybodak, bên dưới cách nó được thực hiện trong môi trường phát triển Android.


1
Đây chính xác là những gì tôi đã tìm kiếm trong 30 phút qua. Cảm ơn rất nhiều!
Geert Schuring

Nếu tên người dùng / mật khẩu của bạn chứa các ký tự đặc biệt, tôi tin rằng bạn sẽ cần urlencode những ký tự đó. Sau đó, trong đoạn mã ở trên, bạn sẽ vượt qua url.getUserInfo()thorugh URLDecoder.decode()trước (@Peter Rader).
m01

Cảm ơn, nhưng đối với Android, bạn nên sử dụng Base64 như sau: String basicAuth = "Basic" + Base64.encodeToString (url.getUserInfo (). GetBytes (), Base64.DEFAULT); httpConn.setRequestProperty ("Ủy quyền", basicAuth);
valerybodak

Cảm ơn bạn valerybodak cho bổ sung đó!
javabeangrinder

8

Khi tôi đến đây để tìm kiếm một câu trả lời Android-Java, tôi sẽ làm một bản tóm tắt ngắn:

  1. Sử dụng java.net.Authenticator như được hiển thị bởi James van Huis
  2. Sử dụng Máy khách HTTP Commons HTTP , như trong Câu trả lời này
  3. Sử dụng java.net.URLCconnectection cơ bản và đặt Tiêu đề xác thực theo cách thủ công như được hiển thị ở đây

Nếu bạn muốn sử dụng java.net.URLCconnectection với Xác thực cơ bản trong Android, hãy thử mã này:

URL url = new URL("http://www.mywebsite.com/resource");
URLConnection urlConnection = url.openConnection();
String header = "Basic " + new String(android.util.Base64.encode("user:pass".getBytes(), android.util.Base64.NO_WRAP));
urlConnection.addRequestProperty("Authorization", header);
// go on setting more request headers, reading the response, etc

1
Cảm ơn. Đã tìm kiếm một câu trả lời cụ thể của Android!
Stephen McCormick

5

Đã có thể đặt auth bằng cách sử dụng HttpsURLCconnectection

           URL myUrl = new URL(httpsURL);
            HttpsURLConnection conn = (HttpsURLConnection)myUrl.openConnection();
            String userpass = username + ":" + password;
            String basicAuth = "Basic " + new String(Base64.getEncoder().encode(userpass.getBytes()));
            //httpsurlconnection
            conn.setRequestProperty("Authorization", basicAuth);

một vài thay đổi được lấy từ bài viết này và Base64 là từ gói java.util.


3

Hãy thực sự cẩn thận với cách tiếp cận "Base64 (). Encode ()", nhóm của tôi và tôi đã gặp 400 vấn đề yêu cầu xấu của Apache vì nó thêm \ r \ n vào cuối chuỗi được tạo.

Chúng tôi tìm thấy nó đánh hơi các gói nhờ vào Wireshark.

Đây là giải pháp của chúng tôi:

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

HttpGet getRequest = new HttpGet(endpoint);
getRequest.addHeader("Authorization", "Basic " + getBasicAuthenticationEncoding());

private String getBasicAuthenticationEncoding() {

        String userPassword = username + ":" + password;
        return new String(Base64.encodeBase64(userPassword.getBytes()));
    }

Hy vọng nó giúp!


3

Sử dụng mã này để xác thực cơ bản.

URL url = new URL(path);
String userPass = "username:password";
String basicAuth = "Basic " + Base64.encodeToString(userPass.getBytes(), Base64.DEFAULT);//or
//String basicAuth = "Basic " + new String(Base64.encode(userPass.getBytes(), Base64.No_WRAP));
HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.setRequestProperty("Authorization", basicAuth);
urlConnection.connect();


2

Tôi muốn cung cấp câu trả lời cho trường hợp bạn không có quyền kiểm soát mã mở kết nối. Giống như tôi đã làm khi sử dụng URLClassLoaderđể tải tệp jar từ máy chủ được bảo vệ bằng mật khẩu.

Các Authenticatorgiải pháp sẽ làm việc nhưng có nhược điểm mà nó đầu tiên cố gắng để đạt được các máy chủ mà không có một mật khẩu và chỉ sau khi máy chủ yêu cầu một mật khẩu cung cấp một. Đó là một roundtrip không cần thiết nếu bạn đã biết máy chủ sẽ cần mật khẩu.

public class MyStreamHandlerFactory implements URLStreamHandlerFactory {

    private final ServerInfo serverInfo;

    public MyStreamHandlerFactory(ServerInfo serverInfo) {
        this.serverInfo = serverInfo;
    }

    @Override
    public URLStreamHandler createURLStreamHandler(String protocol) {
        switch (protocol) {
            case "my":
                return new MyStreamHandler(serverInfo);
            default:
                return null;
        }
    }

}

public class MyStreamHandler extends URLStreamHandler {

    private final String encodedCredentials;

    public MyStreamHandler(ServerInfo serverInfo) {
        String strCredentials = serverInfo.getUsername() + ":" + serverInfo.getPassword();
        this.encodedCredentials = Base64.getEncoder().encodeToString(strCredentials.getBytes());
    }

    @Override
    protected URLConnection openConnection(URL url) throws IOException {
        String authority = url.getAuthority();
        String protocol = "http";
        URL directUrl = new URL(protocol, url.getHost(), url.getPort(), url.getFile());

        HttpURLConnection connection = (HttpURLConnection) directUrl.openConnection();
        connection.setRequestProperty("Authorization", "Basic " + encodedCredentials);

        return connection;
    }

}

Điều này đăng ký một giao thức mới myđược thay thế bằng httpkhi thông tin đăng nhập được thêm vào. Vì vậy, khi tạo mới URLClassLoaderchỉ thay thế httpvới myvà mọi thứ đều tốt. Tôi biết URLClassLoadercung cấp một hàm tạo có một URLStreamHandlerFactorynhưng nhà máy này không được sử dụng nếu URL trỏ đến tệp jar.


1

Kể từ Java 9, bạn có thể làm điều này

URL url = new URL("http://www.example.com");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setAuthenticator(new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication ("USER", "PASS".toCharArray());
    }
});

2
Không thấy setAuthenticator()phương pháp.
WesternGun

0

ANDROD THỰC HIỆN Một phương pháp hoàn chỉnh để yêu cầu phản hồi dữ liệu / chuỗi từ dịch vụ web yêu cầu ủy quyền với tên người dùng và mật khẩu

public static String getData(String uri, String userName, String userPassword) {
        BufferedReader reader = null;
        byte[] loginBytes = (userName + ":" + userPassword).getBytes();

        StringBuilder loginBuilder = new StringBuilder()
                .append("Basic ")
                .append(Base64.encodeToString(loginBytes, Base64.DEFAULT));

        try {
            URL url = new URL(uri);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.addRequestProperty("Authorization", loginBuilder.toString());

            StringBuilder sb = new StringBuilder();
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = reader.readLine())!= null){
                sb.append(line);
                sb.append("\n");
            }

            return  sb.toString();

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (null != reader){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

0

tôi đã làm theo cách này bạn cần làm điều này chỉ cần sao chép dán nó là hạnh phúc

    HttpURLConnection urlConnection;
    String url;
 //   String data = json;
    String result = null;
    try {
        String username ="danish.hussain@gmail.com";
        String password = "12345678";

        String auth =new String(username + ":" + password);
        byte[] data1 = auth.getBytes(UTF_8);
        String base64 = Base64.encodeToString(data1, Base64.NO_WRAP);
        //Connect
        urlConnection = (HttpURLConnection) ((new URL(urlBasePath).openConnection()));
        urlConnection.setDoOutput(true);
        urlConnection.setRequestProperty("Content-Type", "application/json");
        urlConnection.setRequestProperty("Authorization", "Basic "+base64);
        urlConnection.setRequestProperty("Accept", "application/json");
        urlConnection.setRequestMethod("POST");
        urlConnection.setConnectTimeout(10000);
        urlConnection.connect();
        JSONObject obj = new JSONObject();

        obj.put("MobileNumber", "+97333746934");
        obj.put("EmailAddress", "danish.hussain@mee.com");
        obj.put("FirstName", "Danish");
        obj.put("LastName", "Hussain");
        obj.put("Country", "BH");
        obj.put("Language", "EN");
        String data = obj.toString();
        //Write
        OutputStream outputStream = urlConnection.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
        writer.write(data);
        writer.close();
        outputStream.close();
        int responseCode=urlConnection.getResponseCode();
        if (responseCode == HttpsURLConnection.HTTP_OK) {
            //Read
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));

        String line = null;
        StringBuilder sb = new StringBuilder();

        while ((line = bufferedReader.readLine()) != null) {
            sb.append(line);
        }

        bufferedReader.close();
        result = sb.toString();

        }else {
        //    return new String("false : "+responseCode);
        new String("false : "+responseCode);
        }

    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (JSONException e) {
        e.printStackTrace();
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.