رفتن به مطلب

ثبت نام ، ورود و ارسال درخواست امن با توکن رتروفیت


hossein_DW

ارسال‌های توصیه شده

سلام 

مشکل در ثبت نام، ورود و ارسال درخواست های امن از مواردیه که زیاد در موردش پست میبینم و چون مبحث امنیت مبحث خیلی مهمیه که تا قبل از یادگیریش، خودمم برای گرفتن پروژه استرس های زیادی داشتم به این فکر افتادم که دانش و تجربه خودم در این زمینه رو با شما برنامه نویسان عزیز در میون بزارم.و برای ارسال درخواست هامون به سرور چه کتابخونه ای بهتر،منظم تر،زیبا تر، فوق العاده تر و غیره تر از retrofit2 نازنین :bigsmile: .

 

خب اول کد رو قرار میدم بعدش میرم سراغ توضیحات .

1. import android.content.Context;
2. import android.content.SharedPreferences;
3. import java.util.concurrent.TimeUnit;
4. import okhttp3.OkHttpClient;
5. import okhttp3.Request;
6. import project.main.G;
7. import project.utils.Config;
8. import project.utils.Constant;
9. import retrofit2.Retrofit;
10. import retrofit2.converter.gson.GsonConverterFactory;
11.
12.
13.
14. public class ApiClient {
15. private static Retrofit apiClient;
16. private ApiClient () {
17.  }
18. public static Retrofit getInstance() {
19.  if (apiClient == null ) {
20.  final SharedPreferences preferences = G.context.getSharedPreferences(Constant.LOCATION_PREF, Context.MODE_PRIVATE);
21.  OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
22.  .retryOnConnectionFailure(true)
23.  .readTimeout(20, TimeUnit.SECONDS)
24.  .connectTimeout(20, TimeUnit.SECONDS)
25.  .addInterceptor(chain -> {
26.  Request oldRequest = chain.request();
27.  Request.Builder newRequestBuilder = oldRequest.newBuilder();
28.   newRequestBuilder.addHeader("Accept","application/json");
29.   if (preferences.getString(Constant.PRE_USER_TOKEN,null) != null ) {
30.   newRequestBuilder.addHeader("token", preferences.getString(Constant.PRE_USER_TOKEN,null));
31.   }
32.   newRequestBuilder.method(oldRequest.method(),oldRequest.body());
33.   return chain.proceed(newRequestBuilder.build());
34.  })
35   .build();
36. apiClient = new Retrofit.Builder()
37.  .baseUrl(Config.BASE_URL)
38.  .addConverterFactory(GsonConverterFactory.create())
39.  .client(okHttpClient)
40.  .build();
41.  }
 42. return apiClient;
}

خط 19 با چک کردن نال بودن رتروفیت شروع به ساخت آن می کنیم

خط 20 با استفاده از کلاس Constantو متغیر استاتیک LOCATION_PREF و صد البته MODE_PRIVATE سراغ شیرد پرفرنس خود میرویم.

خط 21 با ایجاد یک نمونه از کلاس OkHttpClient متد های مورد نیاز را صدا میزنیم.

خط 25 با صدا زدن متد addInterceptor یک وقفه در ارسال درخواست ایجاد می کنیم(این وقفه به شکلی نیست که به نظر بیاید) تا بتوانیم به درخواست خود مقادیری اضافه کنیم.chain  موجود در این خط حاوی اطلاعاتی است که ما به سرور ارسال می کنیم.

خط 26 یک Request از کلاس OkHttpClient ایجاد کرده و با صدا زدن متد request از chain  به درخواست دسترسی پیدا می کنیم.

خط 27 با استفاده از دیزاین پترن Builder یک درخواست جدید ایجاد می کنیم. با این دیزاین پترن به Request دسترسی پیدا خواهیم کرد و میتوانیم آن را تغییر دهیم.

خط 28 با استفاده از متد addHeader میتوان هدر های متفاوتی ارسال کرد . در این خط ما نوع دریافت اطلاعات از سرور را تعیین می کنیم.

خط 29 الی 31 قسمت اصلی کار ما بحساب می آید. در این سه خط چک می کنیم که اگر کاربر توکن را در شیرد پرفرنسس موجود داشت، بعنوان هدر آن را ارسال کن.

سوال؟ چرا این کار را در interface انجام نمی دهیم. 1-وقتی کلاس قدرتمند OkHttpClient این امکان را در اختیار ما قرار داده چرا از آن استفاده نکنیم.2-فرض کنید در interface خود چندین درخواست به ادرس های مختلف سایت خود دارید که لازم است هدر را برای هر کدام ست کنید ،ترجیح میدهید کد های ارسال هدر را هر بار برای هر درخواست در interface قرار دهید و از خوانایی کد خود کم کنید یا آن را یک بار در کلاس اصلی خود بنویسید؟!

خط 32 از اونجایی که فقط نیاز به تغییر و افزودن هدر به درخواست خود داشتیم، با صدا زدن متد method دو پارامتر method و body را از oldRequest میگیریم و نه از newRequestBuilder.

خط 33 در این خط chain را با newRequestBuilder proceed  میکنیم و نتیجه را return 

کد هایی که تا به این جای کار زدیم بدین معنی است که ما کاری با درخواست و یا نوع درخواست POST یا GET نداریم فقط به اصطلاح توقفی ایجاد کرده و توکن خود را (برای امنیت بیشتر، رعایت نظم و خلاصه کردن کد نویسی) بعنوان هدر ارسال می کنیم.

خط 39 (هرگز نشه فراموش:) ) شرط انجام تمام مراحل بالا صدا زدن متد client و افزودن okHttpClient به آن است.

 

خط 30 بسیار مهم و اشکال زا است. توجه داشته باشید هر مدت addHeader دو پارامتر name و value دارد و ما در این خط توکن خود را با "token" نام گذاری کردیم. برای دریافت امن این توکن در سرور لازم است آن را به شکل زیر دریافت کنید.

$header = $_SERVER["HTTP_TOKEN"];

استفاده از عبارت HTTP_ و سپس نامی که در زمان ارسال توکن برایش انتخاب کردیم (البته با حروف بزرگ)  لازم الاجراست (اسم توکن رو بصورت دو تکه نه ارسال و نه دریافت کنید مثل"HTTP_TOKEN_USER" چون بازم خطا میده) در غیر اینصورت توکنی دریافت نخواهد شد(حداقل من نتونستم دریافت کنم، این PHP کلی مسخره بازی دراورد سرش و پوست از سرم کند).

نکته وقتی از اندروید Q  و به بعد استفاده می کنیم یا باید از پروتکل HTTPS اطلاعات رو دریافت کنید یا به فایل منیفست خود رفته و در تگ اپلیکیشن دو مورد زیر رو اضافه کنید.

android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"

در آموزش های بعدی سراغ OAuth2 (ساخت توکن،رفرش توکن ، تاریخ انقضای توکن ارسال اطلاعات خصوصی در ازای توکن صحیح) ،پروگارد، استفاده از HashMap  برای سهولت در استفاده از درخواست های رتروفیت و ... میریم.

 

import android.content.Context;
import android.content.SharedPreferences;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import project.main.G;
import project.utils.Config;
import project.utils.Constant;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * Created by Asus on 4/16/2019.
 */

public class ApiClient {

  private  static Retrofit apiClient;

  private ApiClient () {

  }


  public static  Retrofit getInstance() {


    if (apiClient == null ) {
      final SharedPreferences preferences = G.context.getSharedPreferences(Constant.LOCATION_PREF, Context.MODE_PRIVATE);
      OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
        .retryOnConnectionFailure(true)
        .readTimeout(20, TimeUnit.SECONDS)
        .connectTimeout(20, TimeUnit.SECONDS)
        .addInterceptor(chain -> {
          Request oldRequest = chain.request();
          Request.Builder newRequestBuilder = oldRequest.newBuilder();
          
          newRequestBuilder.addHeader("Accept","application/json");
//            Toast.makeText(G.context, ""+TokenContainer.getToken(), Toast.LENGTH_SHORT).show();
          if (preferences.getString(Constant.PRE_USER_TOKEN,null) != null ) {
            newRequestBuilder.addHeader("token", preferences.getString(Constant.PRE_USER_TOKEN,null));
          }

          newRequestBuilder.method(oldRequest.method(),oldRequest.body());

          return chain.proceed(newRequestBuilder.build());
        })
        .build();



      apiClient = new Retrofit.Builder()
        .baseUrl(Config.BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .client(okHttpClient)
        .build();
    }
    return apiClient;
  }

 

 

لینک ارسال
به اشتراک گذاری در سایت های دیگر

ممنون بابت آموزش خوبتون
ولی این روش هم امنیت خاصی نداره و داده ها مانیتور میشه
بهترین راه تا الان که بنده تست گرفتم و هیچکس نتونسته مانیتور و خرابکاری کنه SSL بروی هاست یا سرور هست و اتصال HTTPS در کلاینت

 

لینک ارسال
به اشتراک گذاری در سایت های دیگر

در 3 ساعت قبل، mohammad-alone گفته است :

ممنون بابت آموزش خوبتون
ولی این روش هم امنیت خاصی نداره و داده ها مانیتور میشه
بهترین راه تا الان که بنده تست گرفتم و هیچکس نتونسته مانیتور و خرابکاری کنه SSL بروی هاست یا سرور هست و اتصال HTTPS در کلاینت

 

ممنون.

خوب در هر صورت که ما باید توکن رو برای چک کردن کاربر ارسال کنیم و تا جایی که من متوجه شدم ارسال توکن با روشی که گفتم مشکلی نداره، فقط طبق نظر شما باید با HTTPS انجام بشه درسته؟! خوشحال میشم در مورد روشتون کمی بیشتر توضیح بدید.

لینک ارسال
به اشتراک گذاری در سایت های دیگر

بایگانی شده

این موضوع بایگانی و قفل شده و دیگر امکان ارسال پاسخ نیست.

  • کاربران آنلاین در این صفحه   0 کاربر

    • هیچ کاربر عضوی،در حال مشاهده این صفحه نیست.
×
×
  • اضافه کردن...