Skip to content
Ali Zeynali edited this page Jun 8, 2019 · 6 revisions

پروژه‌ی آموزشی نحوه‌ استفاده از SDK نقشه نشان

Volley

Volley یکی دیگر از کتابخانه‌هایی است که برای انجام یک درخواست REST API در اندروید قابل استفاده است. در این بخش از پروژه، دقیقا کاری که در بخش قبل (Retrofit) انجام داده‌ایم را با Volley انجام می‌دهیم. پس از لمس طولانی در یک نقطه و رسم یک نشانگر در آن‌ نقطه، نام محله و آدرس آن نقطه در Bottom sheet نوشته خواهد شد.

توضیحات اولیه در مورد وب‌سرویس «تبدیل موقعیت به آدرس» و همچنین بخش‌هایی از کد که در بخش (Retrofit) آمده بود، مجددا توضیح داده نخواهد شد.



  • build.gradle (Module: app):

برای استفاده از کتابخانه Volley، وابستگی زیر را به مجموعه وابستگی‌های این فایل اضافه کنید:

    implementation 'com.android.volley:volley:1.0.0'


  • APIVolley.java:

تنها بخشی متفاوت این کد، نحوه پیاده‌سازی متد neshanReverseAPI است.

ابتدا آدرس درخواست (با طول و عرض جغرافیایی مورد نظر) به دست می‌آید و در متغیر requestURL ذخیره می‌شود. سپس طول و عرض جغرافیایی با ۶ رقم اعشار و جدا شده از هم با استفاده از یک ویرگول به یک رشته تبدیل می‌شود تا در زمانی که وب‌سرویس قادر به تشخیص آدرس مکانی نباشد، این رشته به جای آدرس آن مکان نشان داده شود.

یک شی از نوع StringRequest با نام reverseGeoSearchRequest ساخته می‌شودو به عنوان ورودی به آن، نوع درخواست - که در اینجا GET است- و آدرس درخواست - که در بالاتر آماده شد - و یک Listener برای پاسخ و یک Listener برای خطا داده می‌شود.

متد onResponse به صورت زیر Override می‌شود که ابتدا یک JSONObject از رشته پاسخ ورودی این متد ساخته می‌شود و رشته همسایگی و آدرس از آن دریافت می‌شود. در صورتی که رشته دریافتی برای همسایگی و آدرس برابر با رشته null نباشد، یعنی همسایگی و آدرس به درستی دریافت شده است، در این صورت این اطلاعات در Bottomsheet ذخیره نشان داده می‌شود. در صورتی که یکی از این دو مورد برابر با رشته null باشد یا Exception ای رخ دهد، رشته ساخته شده از روی طول و عرض جغرافیایی در Bottom sheet نمایش داده می‌شود.

در ادامه در متد getHeaders یک Map<String, String> ساخته و با استفاده از متد put کلید API درون Header درخواست قرار می‌گیرد.

در پایان متد newRequestQueue از کلاس Volley صدا زده می‌شود و با استفاده از متد add، درخواست رشته‌ای ساخته شده به صف درخواست‌ها اضافه می‌شود.

توضیحات مربوط به پین کردن کلید عمومی را می‌توانید در بخش Certificate Pinning مطالعه کنید.

    private void neshanReverseAPI(LngLat loc) {
        String requestURL = "https://api.neshan.org/v1/reverse?lat=" + loc.getY() + "&lng=" + loc.getX();
        final String latLngAddr = String.format("%.6f", loc.getY()) + "," + String.format("%.6f", loc.getX());

        TrustManager tm[] = {new PubKeyManager("30820222300d06092a864886f70d01010105000382020f003082020a0282020100ab724ce2a6ba385a561fe3ccfe6212fcabdc75abc8df6ef4109050a0a22a200c709b2fccb386bf616b78da297aac36429b644f9bc4ec6befd7cdd28eed044eba6fdd4114547e75156b3c58176ebe24d54977c0796dc2debada5485bf320e15431dc5f150311d0a952b4b6b4775c9e41bb653d5d387bad9b952baa8d016e1231b8fd45bcbc5cabb5be16da57bcb621514e08caa77f079da049947309bb94d05d335e380a67299cd332e97caf8f7fcd8c7f6cd1563a47e5f2f8b0316f4de3d5e900cbdc9eae7473fbcf0fd5bdb12aa6e001ca29bd6523e38822464814df6b259f132adaf4f0b3e12a2b18810f9d8a8665255c5105ff17995011838f4cc11d4607f771bc7ec9df408ab8fde1152958063db8cd7709b8095dcad69d35ae9912c01ab8aaeb70ccad40d20564ad6eb42c54003ff1de5f20ad9bdb4a4e3638ed0cc5c04dc41529ce4932de21f3f3bbbb8c305d6ba55fc10d5fc9008bdb82bb0f726bd8e6963afc6b1f4a4b863a4b97d3fafb80151cfeb8d768415b9407bfdde6482325971504a87f77c591c0d326b8caad878e1fb45e3c96d65f140becab388d280f76d5e4bbda47ade71526232bafd0e9bd594c46bdafd3f994a82827596292318c35919298325f81f417f0eb27f2da13981669092cbfc2e77e76a055af3b5d0f67a38ffd1b56b48581a2f54ace04ae63ffab13b1a48698c8cb201738fb7fa6c74c23d0203010001")};
        SSLSocketFactory pinnedSSLSocketFactory = null;
        try {
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, tm, null);
            pinnedSSLSocketFactory = context.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }

        RequestQueue requestQueue = Volley.newRequestQueue(this, new HurlStack(null, pinnedSSLSocketFactory));

        StringRequest reverseGeoSearchRequest = new StringRequest(
                Request.Method.GET,
                requestURL,
                new com.android.volley.Response.Listener<String>() {

                    @Override
                    public void onResponse(String response) {
                        try {
                            JSONObject obj = new JSONObject(response);
                            String neighbourhood = obj.getString("neighbourhood");
                            String address = obj.getString("address");

                            // if server was able to return neighbourhood and address to us
                            if(!neighbourhood.equals("null") && !address.equals("null")) {
                                addressTitle.setText(neighbourhood);
                                addressDetails.setText(address);
                            }
                            else{
                                addressTitle.setText("آدرس نامشخص");
                                addressDetails.setText(latLngAddr);
                            }

                        } catch (Exception e) {

                            addressTitle.setText("آدرس نامشخص");
                            addressDetails.setText(latLngAddr);
                        }
                    }
                }, new com.android.volley.Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                error.printStackTrace();
            }
        }){
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String>  params = new HashMap<>();
                // TODO: replace "YOUR_API_KEY" with your api key
                params.put("Api-Key", "YOUR_API_KEY");
                return params;
            }
        };

        // Add the request to the queue
        requestQueue.add(reverseGeoSearchRequest);
    }


Certificate Pinning (Public Key Pinning)

پین کردن کلید عمومی با استفاده از کتابخانه Volley به اندازه همین کار با استفاده از کتابخانه‌های Retrofit و OkHttp سرراست نیست. برای این‌کار کلاس PubKeyManager - که کلاس X509TrustManager را implement می‌کند - نوشته شده است.

در این checkServerTrusted بررسی می‌شود که اولین کلیدی که از شی chain - که در آن مجموعه مجوز‌های سرور‌هایی که در یک درخواست با آن‌ها ارتباط برقرار می‌شود وجود دارد - با کلیدی که در متغیر publicKey قرار دارد و توسط سازنده این کلاس مقداردهی شده است برابر باشد.

کلید عمومی سرور نشان در فایل APIVolley.java با استفاده از کلاس PubKeyManager پین شده است. در صورتی که کلید سرور دیگری را نیاز داشته باشید، می‌توانید در هنگام اجرای یک درخواست به آن سرور، با لاگ کردن مقدار encoded در متد checkServerTrusted از کلاس PubKeyManager، کلید عمومی سرور خود را به دست آورید.

import android.util.Log;

import java.math.BigInteger;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;

import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public final class PubKeyManager implements X509TrustManager {

    private String publicKey;

    public PubKeyManager(String publicKey) {
        this.publicKey = publicKey;
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        if (chain == null) {
            throw new IllegalArgumentException("checkServerTrusted: X509Certificate array is null");
        }
        if (!(chain.length > 0)) {
            throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
        }

        // Perform customary SSL/TLS checks
        TrustManagerFactory tmf;
        try {
            tmf = TrustManagerFactory.getInstance("X509");
            tmf.init((KeyStore) null);

            for (TrustManager trustManager : tmf.getTrustManagers()) {
                ((X509TrustManager) trustManager).checkServerTrusted(
                        chain, authType);
            }

        } catch (Exception e) {
            throw new CertificateException(e.toString());
        }

        // Hack ahead: BigInteger and toString(). We know a DER encoded Public
        // Key starts with 0x30 (ASN.1 SEQUENCE and CONSTRUCTED), so there is
        // no leading 0x00 to drop.
        RSAPublicKey pubkey = (RSAPublicKey) chain[0].getPublicKey();
        String encoded = new BigInteger(1 /* positive */, pubkey.getEncoded())
                .toString(16);

        Log.d("cert", "publikKey: " + publicKey);
        Log.d("cert", "pubkey: " + pubkey);
        Log.d("cert", "encoded: " + encoded);


        // Pin it!
        final boolean expected = publicKey.equalsIgnoreCase(encoded);
        // fail if expected public key is different from our public key
        if (!expected) {
            throw new CertificateException("Not trusted");
        }
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }

با استفاده از کلاس PubKeyManager و به صورت زیر، یک شی از کلاس pinnedSSLSocketFactory ساخته شده و به درخواست volley اضافه می‌شود. در این صورت در هر بار اجرا شدن درخواست، ابتدا بررسی می‌شود که کلید عمومی سروری که درخواست به آنجا داده می‌شود، برابر با کلید عمومی سرور نشان باشد.

        TrustManager tm[] = {new PubKeyManager("30820122300d06092a864886f70d01010105000382010f003082010a0282010100b2d2b372f340619bdd691d443d5cc5c4fa458eb02709d232702b29bab76dd91a5fb13de61ba32100604c0071664feb928bafe4226204e605017d92dfbeaff27debf9c9d47709894a53d5717fac9a6c0f562697fc8ffaac1d633fa0c3781bf4d665940340bb603f6b821a460aa730eecb624acc165ab5e765b894938437702cbe582dd038c79c41603034258f675c63beb68b76cb844f916a800d222d5393eead1b1cff218b6a9b7abd71eada18f262b57fd378130bc1dd4ff1558c5d1c1823219b2a35a43cd4c0f178f5b85a00efc7c83dc6cfce8a2a24fba879bc401c276466f0f13fbb16ac70516badb03e1a01676a4a8199be2096f2a09e719de5c084999d0203010001")};
        SSLSocketFactory pinnedSSLSocketFactory = null;
        try {
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, tm, null);
            pinnedSSLSocketFactory = context.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }

        RequestQueue requestQueue = Volley.newRequestQueue(this, new HurlStack(null, pinnedSSLSocketFactory));


صفحه قبل (Retrofit)

صفحه بعد (OkHttp)

Clone this wiki locally