All Products
Search
Document Center

HTTPDNS:Praktik terbaik penggunaan HTTPDNS dengan WebView dan OkHttp di Android

Last Updated:Dec 04, 2025

Dokumen Alur integrasi SDK Android menjelaskan alur lengkap untuk mengimpor dan mengonfigurasi SDK Android, mengurai alamat IP, menerapkan SDK ke pustaka jaringan, serta mengautentikasi integrasi. Topik ini menjelaskan solusi spesifik untuk mengintegrasikan HTTPDNS dalam skenario WebView Android.

1. Latar Belakang

HTTPDNS Alibaba Cloud adalah solusi efektif untuk menghindari pembajakan DNS. Saat memulai permintaan jaringan, Anda dapat melewati resolusi DNS default sistem dengan menggunakan API yang disediakan oleh HTTPDNS, sehingga mengurangi atau menghilangkan risiko pembajakan DNS.

Pada halaman web yang dimuat oleh WebView, ketika halaman web memulai permintaan jaringan, kita dapat mencegah permintaan tersebut di dalam WebView dan membiarkan lapisan native (Aplikasi) mengeksekusi permintaan jaringan sebenarnya. Selama proses ini, Anda dapat menggunakan hasil resolusi yang disediakan oleh HTTPDNS untuk menggantikan resolusi DNS sistem guna mencegah pembajakan DNS.

Saat ini, pustaka permintaan jaringan utama di Android adalah OkHttp, sehingga dokumen ini berfokus pada implementasi OkHttp.

Penting
  • Dokumen ini merupakan solusi referensi untuk mengakses HTTPDNS dalam skenario Android WebView. Kode terkait yang diberikan adalah kode referensi, bukan kode yang dapat digunakan langsung di lingkungan produksi online. Kami menyarankan Anda membaca dokumen ini dengan cermat dan membuat penilaian yang masuk akal sebelum implementasi.

  • Sistem Android bervariasi menurut penyedia layanan. Oleh karena itu, kami menyarankan Anda terlebih dahulu menerapkan solusi yang dijelaskan dalam topik ini di lingkungan pengujian dan memeriksa apakah ada pengecualian. Jika Anda memiliki pertanyaan, berikan umpan balik kepada kami melalui Dukungan teknis, sehingga kami dapat mengoptimalkannya tepat waktu.

  • Dokumen ini hanya menjelaskan cara menggunakan IP yang diresolusi oleh HTTPDNS untuk membuat permintaan dalam skenario WebView. Untuk memahami layanan resolusi HTTPDNS itu sendiri, harap terlebih dahulu merujuk ke Integrasi SDK Android.

2. Keterbatasan dan dampak WebView hanya mencegat permintaan GET

Di WebView Android, melalui metode WebViewClient.shouldInterceptRequest(), kita hanya bisa mendapatkan informasi rinci tentang permintaan GET. Untuk metode permintaan lainnya seperti POST, body tidak diberikan kepada pengembang, sehingga tidak mungkin sepenuhnya mencegat dan mengganti dengan resolusi HTTPDNS.

Namun, ini tidak berarti bahwa mengakses HTTPDNS tidak berguna, terutama karena alasan-alasan berikut:

  1. Sebagian besar sumber daya statis web menggunakan permintaan GET Umumnya, sumber daya statis yang dibutuhkan oleh halaman web (seperti gambar, skrip, lembar gaya, dll.) hampir semuanya diperoleh melalui permintaan GET. Dengan mencegat permintaan GET ini dan menggunakan HTTPDNS, proses pemuatan sumber daya utama dapat ditangani, sangat mengurangi risiko pembajakan.

  2. Proporsi yang cukup besar dari panggilan API juga menggunakan GET Banyak antarmuka (terutama antarmuka tipe query atau yang dapat di-cache) menggunakan permintaan GET. Oleh karena itu, mencegat permintaan ini juga dapat menghindari pembajakan DNS melalui HTTPDNS.

  3. Sumber daya sebagian besar di-hosting di CDN, HTTPDNS meningkatkan penjadwalan terdekat Sebagian besar sumber daya statis biasanya didistribusikan di node CDN, dan kemampuan CDN untuk mengakses secara lokal bergantung pada akurasi resolusi nama domain. Melalui HTTPDNS, Anda dapat melewati pembajakan DNS sistem untuk memastikan bahwa resolusi nama domain mengarah ke node CDN optimal, memungkinkan pengguna mendapatkan kecepatan akses dan pengalaman yang lebih baik.

  4. Hanya mencegat permintaan GET juga dapat menghindari sebagian besar skenario pembajakan Pembajakan DNS sering kali menargetkan nama domain populer atau nama domain sumber daya statis massal, dan akses ke nama domain ini hampir semuanya adalah permintaan GET. Meskipun saat ini HTTPDNS hanya dapat digunakan pada permintaan GET, ini dapat mencakup sebagian besar lalu lintas, mencapai 80% atau bahkan perlindungan yang lebih tinggi.

Secara keseluruhan, meskipun pada tingkat WebView, saat ini hanya permintaan GET yang dapat dicegat dan ditulis ulang, dalam bisnis aktual, metode ini biasanya mencakup lalu lintas utama dan mencapai penjadwalan terdekat yang lebih akurat dalam skenario CDN, sehingga memainkan peran penting dalam meningkatkan keamanan dan kinerja.

3. Kode Demo

HTTPDNS+WebView+OkHttp praktik terbaik kode lengkap dapat ditemukan di Demo Android WebView+HTTPDNS+OkHttp.

4. Deskripsi implementasi

Langkah-langkah berikut menunjukkan cara mengakses HTTPDNS berdasarkan OkHttp dan WebView, serta mencegat dan memproses permintaan yang dimulai oleh halaman web di lapisan native.

4.1 Konfigurasi OkHttp

4.1.1 Resolusi DNS kustom

OkHttp menyediakan antarmuka Dns, yang memungkinkan pemanggil untuk menyesuaikan resolusi DNS. Anda dapat mengintegrasikan HTTPDNS ke dalam API ini untuk mengimplementasikan resolusi DNS kustom. Gunakan kode contoh berikut:

OkHttpClient.Builder()
        // Sesuaikan logika resolusi DNS.
        .dns(object : Dns {
            override fun lookup(hostname: String): List<InetAddress> {
                val inetAddresses = mutableListOf<InetAddress>()
                HttpDns.getService(accountId)
                    .getHttpDnsResultForHostSyncNonBlocking(hostname, RequestIpType.auto)?.apply {
                    if (!ipv6s.isNullOrEmpty()) {
                        for (i in ipv6s.indices) {
                            inetAddresses.addAll(
                                InetAddress.getAllByName(ipv6s[i]).toList()
                            )
                        }
                    } else if (!ips.isNullOrEmpty()) {
                        for (i in ips.indices) {
                            inetAddresses.addAll(
                                InetAddress.getAllByName(ips[i]).toList()
                            )
                        }
                    }
                }

                if (inetAddresses.isEmpty()) {
                    inetAddresses.addAll(Dns.SYSTEM.lookup(hostname))
                }
                return inetAddresses
            }
        })
        .build()
new OkHttpClient.Builder()
        // Sesuaikan logika resolusi DNS.
        .dns(new Dns() {
            @NonNull
            @Override
            public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
                ArrayList<InetAddress> inetAddresses = new ArrayList<>();
                HTTPDNSResult result = HttpDns.getService(accountiD)
                        .getHttpDnsResultForHostSync(hostname, RequestIpType.auto);
                if (result != null) {
                    if (result.getIpv6s() != null && result.getIpv6s().length > 0) {
                        for (int i = 0; i < result.getIpv6s().length; i++) {
                            InetAddress[] ipV6InetAddresses = InetAddress.getAllByName(result.getIpv6s()[i]);
                            inetAddresses.addAll(Arrays.asList(ipV6InetAddresses));
                        }

                    } else if (result.getIps() != null && result.getIps().length > 0) {
                        for (int i = 0; i < result.getIps().length; i++) {
                            InetAddress[] ipV4InetAddresses = InetAddress.getAllByName(result.getIps()[i]);
                            inetAddresses.addAll(Arrays.asList(ipV4InetAddresses));
                        }
                    }
                }
                if (inetAddresses.isEmpty()) {
                    inetAddresses.addAll(Dns.SYSTEM.lookup(hostname));
                }
                return inetAddresses;
            }
        })
        .build();
Catatan

Kami sarankan Anda menggunakan DNS lokal sebagai logika dasar untuk resolusi nama domain ketika resolusi nama domain HTTPDNS gagal.

4.1.2 Nonaktifkan pengalihan

Dalam solusi ini, pengalihan tidak ditangani di lapisan jaringan. Sebaliknya, WebView menangani pengalihan berdasarkan perilaku default-nya. Pendekatan ini mencegah pengecualian seperti error cookie dan kegagalan pemuatan resource dalam skenario pengalihan. Contoh implementasinya sebagai berikut:

OkHttpClient.Builder()
    .followRedirects(false)      // Nonaktifkan pengalihan HTTP.
    .followSslRedirects(false)   // Nonaktifkan pengalihan HTTPS.
    .build()
new OkHttpClient.Builder()
    .followRedirects(false)      // Nonaktifkan pengalihan HTTP.
    .followSslRedirects(false)   // Nonaktifkan pengalihan HTTPS.
    .build();
Catatan

Di lingkungan jaringan yang tidak tepercaya, permintaan pengalihan masih dapat dimanipulasi. Anda harus menerapkan perlindungan keamanan di sisi klien.

4.2 WebView mencegat permintaan jaringan

4.2.1 Implementasikan WebViewClient

WebView menyediakan antarmuka WebViewClient. Dengan menimpa metode shouldInterceptRequest(), Anda dapat mencegat permintaan pemuatan sumber daya dari halaman web. Melalui metode ini, Anda dapat mengubah permintaan yang dimulai oleh halaman web untuk menggunakan lapisan native (OkHttp) untuk meminta data, lalu mengembalikan hasilnya ke WebView. Kode contoh:

webview.webViewClient = object : WebViewClient() {
    override fun shouldInterceptRequest(
        view: WebView?,
        request: WebResourceRequest?
    ): WebResourceResponse? {
        if (shouldIntercept(request)) {
            return getResponseByOkHttp(request)
        }
        return super.shouldInterceptRequest(view, request)
    }
}
webview.setWebViewClient(new WebViewClient(){
    @Nullable
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
        if (shouldIntercept(request)) {
            return getResponseByOkHttp(request);
        }
        return super.shouldInterceptRequest(view, request);
    }
});

4.2.2 Tentukan apakah akan mencegat permintaan

Dalam metode shouldIntercept(), pencegatan hanya dilakukan dalam kasus berikut:

  • Hanya mencegat permintaan dengan protokol http atau https. Protokol lainnya tidak dicegat dan masih ditangani oleh WebView itu sendiri.

  • Hanya mencegat permintaan GET (untuk alasan, lihat di atas: "Keterbatasan dan dampak WebView hanya mencegat permintaan GET").

Kode contoh:

private fun shouldIntercept(webResourceRequest: WebResourceRequest?): Boolean {
    if (webResourceRequest == null) {
        return false
    }
    val url = webResourceRequest.url ?: return false
    // Permintaan non-HTTP tidak dicegat.
    if ("https" != url.scheme && "http" != url.scheme) {
        return false
    }
    // Hanya permintaan GET yang dicegat.
    if ("GET".equals(webResourceRequest.method, true)) {
        return true
    }
    return false
}
private boolean shouldIntercept(WebResourceRequest request) {
    if (request == null || request.getUrl() == null) {
        return false;
    }
    // Permintaan non-HTTP tidak dicegat.
    if (!"http".equals(request.getUrl().getScheme()) && !"https".equals(request.getUrl().getScheme())) {
        return false;
    }
    // Hanya permintaan GET yang dicegat.
    if ("GET".equalsIgnoreCase(request.getMethod())) {
        return true;
    }

    return false;
}

4.2.3 Gunakan OkHttp untuk permintaan jaringan

  • Gunakan OkHttp untuk memulai permintaan jaringan berdasarkan URL permintaan, Header, dan informasi lainnya.

  • Kembalikan hasil respons ke WebView setelah mengenkapsulasinya melalui WebResourceResponse.

Kode contoh:

private fun getResponseByOkHttp(webResourceRequest: WebResourceRequest?): WebResourceResponse? {
    if (webResourceRequest == null) {return null}
    try {
        val url = webResourceRequest.url.toString()
        val requestBuilder =
            Request.Builder().url(url).method(webResourceRequest.method, null)
        val requestHeaders = webResourceRequest.requestHeaders
        if (!requestHeaders.isNullOrEmpty()) {
            requestHeaders.forEach {
                requestBuilder.addHeader(it.key, it.value)
            }
        }
        val response = okHttpClient.newCall(requestBuilder.build()).execute()
        val code = response.code
        if (code != 200) {
            return null
        }
        val body = response.body
        if (body != null) {
            val contentType = body.contentType()
            val encoding = contentType?.charset()
            val mediaType = contentType?.toString()
            var mimeType = "text/plain"
            if (!TextUtils.isEmpty(mediaType)) {
                val mediaTypeElements = mediaType?.split(";")
                if (!mediaTypeElements.isNullOrEmpty()) {
                    mimeType = mediaTypeElements[0]
                }
            }
            val responseHeaders = mutableMapOf<String, String>()
            for (header in response.headers) {
                responseHeaders[header.first] = header.second
            }
            var message = response.message
            if (message.isBlank()) {
                message = "OK"
            }
            val resourceResponse =
                WebResourceResponse(mimeType, encoding?.name(), body.byteStream())
            resourceResponse.responseHeaders = responseHeaders
            resourceResponse.setStatusCodeAndReasonPhrase(code, message)
            return resourceResponse
        }
    } catch (e: Throwable) {
        e.printStackTrace()
    }
    return null
}
private WebResourceResponse getResponseByOkHttp(WebResourceRequest request) {
    try {
        String url = request.getUrl().toString();
        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
                .method(request.getMethod(), null);
        Map<String, String> requestHeaders = request.getRequestHeaders();
        if (requestHeaders != null) {
            for (Map.Entry<String, String> entry : requestHeaders.entrySet()) {
                requestBuilder.addHeader(entry.getKey(), entry.getValue());
            }
        }
        Response response = okHttpClient.newCall(requestBuilder.build()).execute();
        if (200 != response.code()) {
            return null;
        }
        ResponseBody body = response.body();
        if (body != null) {
            MediaType contentType = body.contentType();
            if (contentType != null) {
                Charset encoding = contentType.charset();
                String mediaType = contentType.toString();
                String mimeType = "text/plain";
                if (!TextUtils.isEmpty(mediaType)) {
                    String[] mediaTypeElements = mediaType.split(";");
                    if (mediaTypeElements.length > 0) {
                       mimeType = mediaTypeElements[0];
                    }
                }
                Map<String, String> responseHeaders = new HashMap<>();
                for (String key : response.headers().names()) {
                    responseHeaders.put(key, response.header(key));
                }
                String message = response.message();
                if (TextUtils.isEmpty(message)) {
                    message = "OK";
                }
                WebResourceResponse resourceResponse = new WebResourceResponse(mimeType, encoding.name(), body.byteStream());
                resourceResponse.setResponseHeaders(responseHeaders);
                resourceResponse.setStatusCodeAndReasonPhrase(response.code(), message);
                return resourceResponse;
            }

        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

5. Verifikasi

Setelah integrasi selesai, lihat dokumen Verifikasi integrasi pustaka jaringan yang berhasil dan gunakan metode seperti pengujian tiruan pembajakan atau injeksi kesalahan untuk memverifikasi keberhasilan integrasi.

5. Kesimpulan

  • HTTPDNS dapat secara efektif mengurangi risiko pembajakan DNS. Di WebView, dengan mencegat permintaan dan menggunakan pustaka jaringan native (OkHttp) dikombinasikan dengan HTTPDNS, keamanan resolusi nama domain dapat ditingkatkan secara signifikan.

  • Karena keterbatasan mekanisme WebView, pencegatan hanya dapat diimplementasikan pada permintaan GET. Namun, dalam bisnis aktual, ini sudah mencakup sebagian besar skenario seperti memuat sumber daya statis dan antarmuka GET umum.

  • Kami sangat menyarankan agar pengembang menerapkan solusi yang dijelaskan dalam topik ini di lingkungan pengujian, memperhatikan pengecualian online dan situasi adaptasi, serta membuat optimasi lebih lanjut atau pemrosesan khusus sesuai kebutuhan bisnis.

Jika Anda memiliki lebih banyak pertanyaan atau membutuhkan dukungan teknis, berikan umpan balik kepada kami melalui Dukungan teknis. Kami akan mengoptimalkan pengalaman dan kompatibilitas HTTPDNS.