Skip to content
Mohammad-Reza Azizi edited this page Sep 4, 2018 · 5 revisions

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

OkHttp

یکی دیگر از کتابخانه‌های که با استفاده از آن می‌توان در اندروید یک درخواست HTTP را انجام داد، کتابخانه OkHttp است. در این بخش پروژه، مانند دو بخش قبل (Retrofit و Volley) می‌خواهیم پس از لمس طولانی کاربر و نمایش نشانگر، آدرس نقطه انتخاب شده را با استفاده از وب‌سرویس «تبدیل م» به دست آورده و در bottom sheet نمایش دهیم.

توضیحات داده شده در مورد ساختار درخواست و انواع پاسخ‌های سرور در بخش Retrofit آورده شده است. همچنین بخش‌هایی از این کد که نسبت به کد بخش Retrofit تکراری است مجددا توضیح داده نشده است.



  • build.gradle (Module: app): برای استفاده از کتابخانه OkHttp وابستگی زیر را به لیست وابستگی های این فایل اضافه کنید:
    implementation 'com.squareup.okhttp3:okhttp:3.10.0'


  • APIOkHttp.java: تنها بخش متفاوت در این کد، پیاده‌سازی متد neshanReverseAPI است.

ابتدا رشته درخواست ساخته می‌شود و سپس رشته‌ای شامل طول و عرض جغرافیایی - که با یک ویرگول از هم جدا شده‌اند - ساخته می‌شود. این رشته زمانی استفاده می‌شود که آدرس به درستی از وب‌سرویس دریافت نشود.

یک شی از کلاس OkHttpClient با نام client ساخته می‌شود. کلید عمومی سرور نشان بر روی این کلاینت pin شده است. توضیحات این بخش در بخش Public Key Pinning داده شده است.

یک شی از کلاس Request با نام request ساخته می‌شود. در هنگام ساختن این شی، در سرآمد کلید API خود را وارد می‌کنیم و رشته درخواست را نیز به آن می‌دهیم. سپس بر روی client متد newCall صدا زده می‌شود و request به عنوان ورودی به آن داده می‌شود و در نهایت متد enqueue صدا زده شده و یک Callback جدید به عنوان ورودی به آن داده می‌شود و متد‌های onFailure و onResponse پیاده‌سازی می‌شوند.

در متد onResponse تمامی کارهایی که در هنگام دریافت جواب از سرور نیاز هست که انجام داده شود، نوشته می‌شود. در این‌جا ابتدا بررسی شده است که آیا جواب موفقیت آمیز بوده است یا خیر و در صورت موفقیت آمیز بودن جواب، اطلاعات مربوط به همسایگی و آدرس از پاسخ استخراج شده و در bottom sheet نمایش داده می‌شود. در صورتی که آدرس به درستی از سرور دریافت نشود، طول و عرض جغرافیایی نقطه به عنوان آدرس نمایش داده می‌شود.

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

        // creating a CertificatePinner object and adding public key of neshan.org to it
        CertificatePinner certPinner = new CertificatePinner.Builder()
                .add("*.neshan.org",
                        "sha256/Cyg7e5STKgZCwdABdPZlqO5lQWSE0KbWr624HoIUuUc=")
                .build();

        // adding the created certPinner to OkHttpClient
        OkHttpClient client = new OkHttpClient.Builder()
                .certificatePinner(certPinner)
                .build();

        Request request = new Request.Builder()
                //TODO: replace "YOUR_API_KEY" with your api key
                .header("Api-Key", "YOUR_API_KEY")
                .url(requestURL)
                .build();


        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, final Response response) throws IOException {
                if (!response.isSuccessful()) {
                    throw new IOException("Unexpected code " + response);
                } else {

                    String neighbourhood = "آدرس نامشخص";
                    String address = latLngAddr;

                    try {
                        String jsonData = response.body().string();
                        JSONObject obj = new JSONObject(jsonData);

                        neighbourhood = obj.getString("neighbourhood");
                        address = obj.getString("address");


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

                    }
                    catch (Exception e){
                        Log.d("nehsnaReverse", Log.getStackTraceString(e));
                        neighbourhood = "آدرس نامشخص";
                        address = latLngAddr;
                    }
                    finally {

                        final String fNeighbourhood = neighbourhood;
                        final String fAddrees = address;

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                addressTitle.setText(fNeighbourhood);
                                addressDetails.setText(fAddrees);
                            }
                        });
                    }
                }
            }
        });

    }


Certificate Pinning (Public Key Pinning)

برای افزایش امنیت در هنگام استفاده از وب‌سرویس‌ها و جلوگیری از حملات مرد میانی، می‌توان از Certificate Pinning استفاده کرد. با پیاده‌سازی Certificate Pinning می‌توان تعیین کرد که اولین مقصدی که درخواست کلاینت (تلفن همراهی که وب‌سرویس از آنجا درخواست شده است) یک سرور با یک کلید عمومی یا یک certificate خاص باشد و در غیر این صورت، وب‌سرویس صدا زده نشود.

  1. به دست آوردن کلید عمومی سرور نشان

برای به دست آوردن کلید عمومی سرور نشان، دستورات زیر را در یک فایل با پسوند sh. ذخیره کنید.

#!/bin/bash

certs=`openssl s_client -servername $1 -host $1 -port 443 -showcerts </dev/null 2>/dev/null | sed -n '/Certificate chain/,/Server certificate/p'`

rest=$certs
while [[ "$rest" =~ '-----BEGIN CERTIFICATE-----' ]]
do
 cert="${rest%%-----END CERTIFICATE-----*}-----END CERTIFICATE-----"
 rest=${rest#*-----END CERTIFICATE-----}

 echo `echo "$cert" | grep 's:' | sed 's/.*s:\(.*\)/\1/'`

 echo "$cert" | openssl x509 -pubkey -noout | 
     openssl rsa -pubin -outform der 2>/dev/null | 
     openssl dgst -sha256 -binary | openssl enc -base64
done

وقتی این فایل bash را با ورودی neshan.org اجرا کنید نتیجه زیر را مشاهده می‌کنید:

./getPublicKey.sh neshan.org
/C=US/CN=*.neshan.org
Cyg7e5STKgZCwdABdPZlqO5lQWSE0KbWr624HoIUuUc=
/C=PL/O=Unizeto Technologies S.A./OU=Certum Certification Authority/CN=Certum Domain Validation CA SHA2
S4AbJNGvyS57nzJwv8sPMUML8VHSqH1vbiBftdPcErI=
/C=PL/O=Unizeto Technologies S.A./OU=Certum Certification Authority/CN=Certum Trusted Network CA
qiYwp7YXsE0KKUureoyqpQFubb5gSDeoOoVxn6tmfrU=
/C=PL/O=Unizeto Sp. z o.o./CN=Certum CA
lzasOyXRbEWkVBipZFeBVkgKjMQ0VB3cXdWSMyKYaN4=

رشته Cyg7e5STKgZCwdABdPZlqO5lQWSE0KbWr624HoIUuUc= کلید عمومی سرور neshan.org است که در ادامه از آن برای certificate pinning استفاده خواهد شد.


  1. پین کردن کلید عمومی در فایل APIOkHttp.java

در هنگام ساختن درخواست OkHttp در فایل APIOkHttp.java باید موارد زیر انجام شود.

در این کتابخانه، ابتدا یک شی از کلاس CertificatePinner با نام certPinner ساخته می‌شود و سپس با صدا زدن متد add بر روی شی ساخته شده، یک الگو از آدرس‌ها - در این‌جا "neshan.org.*" - و کلید عمومی این الگوی آدرس‌ها داده می‌شود و در نهایت متد build صدا زده خواهد شد.

در هنگام ساختن شی client - که از کلاس OkHttpClient است - با استفاده از متد certificatePinner، شی certPinner به کلانت OkHttp داده می‌شود.

        CertificatePinner certPinner = new CertificatePinner.Builder()
                .add("*.neshan.org",
                        "sha256/Cyg7e5STKgZCwdABdPZlqO5lQWSE0KbWr624HoIUuUc=")
                .build();

        OkHttpClient client = new OkHttpClient.Builder()
                .certificatePinner(certPinner)
                .build();

        Request request = new Request.Builder()
                //TODO: replace "YOUR_API_KEY" with your api key
                .header("Api-Key", "YOUR_API_KEY")
                .url(requestURL)
                .build();


صفحه قبل (Volley)

صفحه بعد (کش‌کردن نقشه)

Clone this wiki locally