آموزش reCAPTCHA در برنامه نویسی اندروید
سلام دوستان امیدوارم حالتان خوب و سلامت باشید در این سری از آموزش برنامه نویسی اندروید به آموزش reCAPTCHA در برنامه نویسی اندروید می پردازیم شاید reCAPTCHA گوگل را در وب سایت ها و اپلیکیشن های خارجی دیده باشید که عملیات احراز هویت کاربر (User Authentication) را انجام میدهد و با الگوریتم های مختلف بررسی می کند که کاربر آیا واقعا شخص حقیقی است یا اینکه یک ربات است در ادامه با ما همراه باشید.
نحوه کار به چه شکل است ؟
دو کلید به نام Secret Key و Site Key وجود دارد که در ادامه میگیم چگونه این رو به دست بیارید این دو کلید Secret key باید در سرور شما باشد و Site Key در اپلیکیشن شما اگر Secret Key شما لو برود به راحتی می توانند آن را دور بزنند. هر زمانی که یک کاربر request میزند کلید site key به سمت سرور ارسال می شود و یک token برای آن شخص ایجاد می شود و اگر گوگل به گونه ای تشخیص دهد که کاربر ربات نیست هیچ کاری نباید کاربر انجام دهد در صورتی که نتواند شخص را بررسی کند از کاربر می خواهد که یک عملیات را انجام دهد.
وارد لینک زیر شوید و اپلیکیشن خود را ثبت کنید.
1 | https://g.co/recaptcha/androidsignup |
Label را برابر با اپلیکیشن خود قرار دهید. نوع reCAPTCHA را برابر با اندروید قرار دهید و در بخش
در ادامه در یک نمونه مثال کد های Site Key و Secret هست که آنها را بردارید در ادامه با آنها کار داریم.
باید سه کتاب خانه را به پروژه اضافه کنیم.
پس وارد فایل Build.Gradle از نوع Module شده سپس سه خط زیر را اضافه کنید.
1 2 3 4 | implementation 'com.google.android.gms:play-services-safetynet:11.8.0' implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' implementation 'com.android.volley:volley:1.1.0' |
پروژه را sync کنید قند شکن فراموش نشه !
برای تنظیم قندشکن از لینک زیر می توانید کمک بگیرید.
لینک آموزش
قبل از اینکه به سراغ کد اندروید برویم باید سمت سرور یک rest api درست کنیم پس برای اینکار یک فایل php به نام google-recaptcha-verfication.php ایجاد می کنیم و کدهای زیر را در آن قرار میدهیم. باید Curl شما فعال باشد (بیشتر مواقع فعال است)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <?php $ch = curl_init(); $secretKey = 'Your Secret Key here'; $captcha = isset($_POST['recaptcha-response']) && !empty($_POST['recaptcha-response']) ? $_POST['recaptcha-response']: ''; curl_setopt_array($ch, [ CURLOPT_URL => 'https://www.google.com/recaptcha/api/siteverify', CURLOPT_POST => true, CURLOPT_POSTFIELDS => [ 'secret' => $secretKey, 'response' => $captcha, 'remoteip' => $_SERVER['REMOTE_ADDR'] ], CURLOPT_RETURNTRANSFER => true ]); $output = curl_exec($ch); curl_close($ch); $json = json_decode($output); $res = array(); if($json->success){ $res['success'] = true; $res['message'] = 'Success'; }else{ $res['success'] = false; $res['message'] = 'Failed'; } echo json_encode($res); ?> |
در بالا یک متغیر به نام secretKey داریم که باید شما secretKey خودتان را در آن قرار دهید. ابتدا بررسی می کنیم داده که شما به سمت این url پست (post) کردید خالی نباشه سپس به گوگل یک request میزنیم گوگل یک json برای ما برمی گرداند اگر برابر با success بود ما یک jsonarray چاپ می کنیم تا در برنامه بررسی کنیم عملیات موفق بوده یا خیر.
وارد AndroidManifest.xml شده و دسترسی زیر را اضافه کنید.
1 | <uses-permission android:name="android.permission.INTERNET" /> |
یک کلاس از نوع singleton باید ایجاد کنید تا request مربوط به volley در آن انجام شود یک فایل به نام MyApplication.java ایجاد کرده و کدهای زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | import android.app.Application; import android.text.TextUtils; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; /** * Created by JFP on 11/03/18. */ public class MyApplication extends Application { public static final String TAG = MyApplication.class .getSimpleName(); private RequestQueue mRequestQueue; private static MyApplication mInstance; @Override public void onCreate() { super.onCreate(); mInstance = this; } public static synchronized MyApplication getInstance() { return mInstance; } public RequestQueue getRequestQueue() { if (mRequestQueue == null) { mRequestQueue = Volley.newRequestQueue(getApplicationContext()); } return mRequestQueue; } public <T> void addToRequestQueue(Request<T> req, String tag) { req.setTag(TextUtils.isEmpty(tag) ? TAG : tag); getRequestQueue().add(req); } public <T> void addToRequestQueue(Request<T> req) { req.setTag(TAG); getRequestQueue().add(req); } public void cancelPendingRequests(Object tag) { if (mRequestQueue != null) { mRequestQueue.cancelAll(tag); } } } |
کلاس بالا همانطور که گفتیم عملیات request را برای ما انجام میدهد بعد از اینکه کلاس بالا را ساختید وارد AndroidManifest.xml شده و خط زیر را به تگ application اضافه کنید.
1 2 3 | <application android:name=".MyApplication"> </application> |
فایل Colors.xml باز کرده و رنگ های زیر را به آن اضافه کنید.
1 2 3 4 5 6 | <?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#00bbd3</color> <color name="colorPrimaryDark">#0097a7</color> <color name="colorAccent">#FF4081</color> </resources> |
یک فایل به نام dimens.xml در values ایجاد کرده و کدهای زیر را در آن قرار دهید.
1 2 3 | <resources> <dimen name="activity_margin">16dp</dimen> </resources> |
فایل strings.xml را باز کرده و کدهای زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 | <resources> <string name="app_name">reCAPTCHA</string> <string name="feedback">بازخورد</string> <string name="hint_feedback">متنی را برای ما بنویسید !</string> <string name="btn_send">ارسال</string> <string name="title_form">ارسال اطلاعات</string> <string name="desc_form"></string> <string name="message_feedback_done">تایید هویت موفقیت آمیز بود.</string> </resources> |
یک لایه به نام activity_main.xml ایجاد کرده و کدهای زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="info.androidhive.recaptcha.MainActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_main" /> </android.support.design.widget.CoordinatorLayout> |
یک لایه دیگر به نام content_main.xml ایجاد می کنیم این لایه در لایه قبلی include شده است.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | content_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="@dimen/activity_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="info.androidhive.recaptcha.MainActivity" tools:showIn="@layout/activity_main"> <LinearLayout android:id="@+id/layout_feedback_form" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/title_form" android:textColor="#666666" android:textSize="20dp" android:textStyle="bold" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/desc_form" /> <EditText android:id="@+id/input_feedback" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_margin" android:gravity="top" android:hint="@string/hint_feedback" android:lines="5" /> <Button android:id="@+id/btn_send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_margin" style="@style/Widget.AppCompat.Button.Colored" android:text="@string/btn_send" android:textColor="@android:color/white" /> </LinearLayout> <TextView android:id="@+id/message_feedback_done" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="40dp" android:gravity="center" android:padding="@dimen/activity_margin" android:text="@string/message_feedback_done" android:textSize="22dp" android:visibility="gone" /> </LinearLayout> |
و در نهایت MainActivity.java همانند زیر می شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | package ir.programchi.recaptcha; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.StringRequest; import com.google.android.gms.common.api.ApiException; import com.google.android.gms.common.api.CommonStatusCodes; import com.google.android.gms.safetynet.SafetyNet; import com.google.android.gms.safetynet.SafetyNetApi; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import org.json.JSONException; import org.json.JSONObject; import java.util.HashMap; import java.util.Map; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private static final String SAFETY_NET_API_SITE_KEY = "6Lf8z0sUAAAAAP80KqD1U-3e7M_JlOrgWSms5XDd"; private static final String URL_VERIFY_ON_SERVER = "https://programchi.ir/api/google-recaptcha-verfication.php"; @BindView(R.id.input_feedback) EditText inputFeedback; @BindView(R.id.layout_feedback_form) LinearLayout layoutFeedbackForm; @BindView(R.id.message_feedback_done) TextView messageFeedbackDone; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setTitle(getString(R.string.feedback)); getSupportActionBar().setDisplayHomeAsUpEnabled(true); Toast.makeText(getApplicationContext(), "Always check Android Studio `LogCat` for errors!", Toast.LENGTH_LONG).show(); } @OnClick(R.id.btn_send) public void validateCaptcha() { String feedback = inputFeedback.getText().toString().trim(); if (TextUtils.isEmpty(feedback)) { Toast.makeText(getApplicationContext(), "Enter feedback!", Toast.LENGTH_LONG).show(); return; } SafetyNet.getClient(this).verifyWithRecaptcha(SAFETY_NET_API_SITE_KEY) .addOnSuccessListener(this, new OnSuccessListener<SafetyNetApi.RecaptchaTokenResponse>() { @Override public void onSuccess(SafetyNetApi.RecaptchaTokenResponse response) { Log.d(TAG, "onSuccess"); if (!response.getTokenResult().isEmpty()) { verifyTokenOnServer(response.getTokenResult()); } } }) .addOnFailureListener(this, new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { if (e instanceof ApiException) { ApiException apiException = (ApiException) e; Log.d(TAG, "Error message: " + CommonStatusCodes.getStatusCodeString(apiException.getStatusCode())); } else { Log.d(TAG, "Unknown type of error: " + e.getMessage()); } } }); } public void verifyTokenOnServer(final String token) { Log.d(TAG, "Captcha Token" + token); StringRequest strReq = new StringRequest(Request.Method.POST, URL_VERIFY_ON_SERVER, new Response.Listener<String>() { @Override public void onResponse(String response) { Log.d(TAG, response.toString()); try { JSONObject jsonObject = new JSONObject(response); boolean success = jsonObject.getBoolean("success"); String message = jsonObject.getString("message"); if (success) { layoutFeedbackForm.setVisibility(View.GONE); messageFeedbackDone.setVisibility(View.VISIBLE); } else { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); } } catch (JSONException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "Json Error: " + e.getMessage(), Toast.LENGTH_LONG).show(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e(TAG, "Error: " + error.getMessage()); } }) { @Override protected Map<String, String> getParams() { Map<String, String> params = new HashMap<>(); params.put("recaptcha-response", token); return params; } }; MyApplication.getInstance().addToRequestQueue(strReq); } } |
در بالا یک متغیر به نام SAFETY_NET_API_SITE_KEY داریم که شما باید در آن Site Key خود را قرار دهید.
URL_VERIFY_ON_SERVER باید url سرور خودتان را قرار دهید (همان فایل php) در ادامه با استفاده از volley به سمت سرور یک داده رو post می کنیم و سمت سرور بررسی انجام می شود.
از متود SafetyNet برای انجام تایید هویت استفاده می کنیم بگونه این کلاس منتظر می ماند تا request از سرور برگردد سپس عملیات checking بررسی می شود.
موفق باشید.