#احراز_هویت در #اندروید با استفاده از کتابخانه #JWT در #PHP

با سلاموقتتون بخیر بنده قصد دارم در این پست نحوه احراز هویت در اندروید رو پیاده سازی کنم در این آموزش از نرم افزار های Android Studio , Rapid PHP و Xampp# استفاده شده ، برای backend# کار از php استفاده کردم و همین طور از پایگاه داده MySQL استفاده کردم برای اتصال اندروید به php همون طور که میدونیم باید از یه host استفاده کنیم تا بتونیم از گوشی اندرویدی به پایگاه داده مون وصل بشیم برای این کار من از نرم افزار xampp استفاده کردم که این کار رو ، روی سیستم ما شبیه سازی میکنه تا دیگه نیاز نباشه به اینترنت متصل بشیم و host خریداری کنیم این یه خلاصه از کاری که میخواستیم انجام بدیم.در ابتدا توضیحاتی را در مورد احراز هویت بیان میکنیم:در احراز هویت ارسال کننده یا دریافت کننده اطلاعات برای همدیگر اطلاعاتی را ارائه می کنند تا قانع شوند آنها موجودیت هایی هستند که اطلاق می کنند. به نوعی در احراز هویت بررسی می شود که شخص یا … موجودیت هست که ادعا می کند. اگر ارسال کننده یا دریافت کننده اطلاعات نتوانند به درستی برای همدیگر احراز هویت شوند در این میان اعتمادی ایجاد نمی شود که آنها بتوانند با همدیگر تبادل اطلاعات داشته باشند. احراز هویت یا Authentication همانطور که بیان شد یک فرآیند است و این فرآیند هم می تواند بسیار بسیار ساده باشد و هم می تواند بسیار بسیار پیچیده و دشوار باشد. ساده ترین راهکار احراز هویت که همگی ما از آن استفاده کرده ایم ساختار بسیار ساده یک کلید احراز هویت متنی است که ما آن را به عنوان پسورد یا رمز عبور می شناسیم و برای احراز هویت شدن در سیستم های مختلف از آن استفاده می کنیم. اما احراز هویت یا Authentication به تنهایی شامل مراحل مختلفی است که برای بالا بردن سطح امنیتی آن ما این مراحل را به شکل زیر طبقه بندی کرده ایم ، هر چقدر شما از مراحل بیشتری دراحراز هویت یا Authentication استفاده کنید طبیعتا امنیت شما نیز بالاتر خواهد رفت : مرحله اول احراز هویت : چیزی که شما می دانید یا What you know ساده ترین مثال آن را نیز همین رمز عبور یا پسوردی می توانیم تعبیر کنیم که از آن در طی روز ممکن است بارها استفاده کنیم. مهمترین نکته در خصوص این فاکتور احراز هویت یا Authentication این است که اگر چیزی که شما می دانید به عنوان روش احراز هویت استفاده می شود ، بنابراین اگر این چیز را شخص دیگری هم بداند ، آن شخص می تواند به جای شما احراز هویت شود.مرحله دوم احراز هویت : چیزی که شما می دانید یا What you know به این معناست که شما دارای یک دستگاه فیزیکی هستید که این دستگاه شما را احراز هویت می کند و شما نیازی به حفظ کردن چیزی ندارید و صرفا با داشتن این دستگاه احراز هویت خواهید شد ، از نمونه های این دستگاه ها می توانیم به کارت های هوشمند ، توکن های امنیتی و … اشاره کنیم. منطقی که برای فاکتور دوم احراز هویت وجود دارد این است که اگر شما یک کارت هوشمند در کنار خود دارید بنابراین مالک آن نیز هستید و مالک دسترسی های مربوط به آن نیز خواهید بود. مرحله سوم احراز هویت : چیزی که شما هستید یا What you are کاربران ممکن است رمز عبور خود را فراموش کنند ، کاربران ممکن است کارت هوشمند خود را گم کنند اما قطعا فراموش نمی کنند که دست و بدن خود را همراه خود به اینور و آنور ببرند. در فاکتور سوم احراز هویت از اعضا و پارامترهای فیزیکی بدن انسان برای احراز هویت استفاده می شود که برای هر فردی در دنیا منحصر به فرد است . برای مثال در این نوع از احراز هویت از الگوی اثر انگشت ، الگوی صدای شخص ، الگوی مردمک و عنبیه چشم ، الگوی کف دست ، الگوی DNA و … استفاده می شود. مرحله چهارم احراز هویت : کاری که شما می کنید یا What you do کاری که شما می کنید یا What you do در واقع یک نوع احراز هویت یا Authentication بیومتریک است که جزو زیرمجموعه های فاکتور سوم به حساب می آید ، در این روش احراز هویت رفتارهایی که شما انجام می دهید تجزیه و تحلیل می شود و بر حسب آنها تشخیص داده می شود که شخص مورد نظر همانی است که ادعا می کند یا خیر ، یکی از مرسوم ترین روش هایی که در این خصوص استفاده می شود سرعت تایپ کردن پسورد شما است.مرحله پنجم در احراز هویت : احراز هویت چند فاکتوری یا Multifactor Authentication همانطور که از نامش هم پیداست یعنی از چندین فاکتور احراز هویت در احراز هویت افراد استفاده شود. احراز هویت با JWTشاید تا الان واژه JWT به گوشتون خورده باشه. JWT مخفف عبارت json web token هست و در واقع به نوعی از مکانیزم ارسال امن اطلاعات (claim) اشاره می کنه که استفاده گسترده ای مخصوصا در زمینه احراز هویت توی اپلیکیشن های موبایلی داره. امروز تو این مقاله می خوایم با هم ببینیم JWT دقیقا چیه، بهمون چه امکاناتی می ده و چه مزیت هایی نسبت به روش های سنتی ارسال اطلاعات داره.برای فهمیدن کارکرد و مزیت های JWT بهتره اول یه نگاهی به روش سنتی احراز هویت با کوکی و سشن بندازیم.احراز هویت به وسیله کوکی و سشن (روش سنتی)اتفاقی که در روش سنتی احراز هویت (cookie-based authentication) می افتاد در واقع این بود که اول کاربر از طریق پر کردن فرم صفحه لاگین، اطلاعات احراز هویتش رو به سمت سرور ارسال می کرد. بعد از اون سرور وظیفه تایید صلاحیت کاربر از طریق بررسی credential هارو به عهده داشت. اگر همه چیز خوب پیش می رفت و اطلاعات تایید می شد اپلیکیشن سمت سرور اطلاعات کاربر رو در سشن قرار می داد و آی دی سشن و همینطور در صورت درخواست قبلی کاربر برای فراموش نکردن رمز عبور اطلاعات خاصی مبنی بر لاگین موندن کاربر توی کوکی ست می شد. در واقع تمام کاری که این وسط انجام می شد به طور خلاصه به این شکل بود:ارسال request حاوی اطلاعات ورود کاربر به سمت سرورپاسخ سرور به همراه آی دی سشن کاربرست شدن آی دی سشن در کوکی کاربرست شدن کوکی لاگین برای لاگین های بعدی و ثبت توکن در سرورمراجعه دوباره به دیتابیس برای بررسی صحت توکن موجود در کوکی در درخواست های بعدیمکانیزم کلی روش مبتنی بر کوکی رو می تونید تو عکس پایین ببینید:روش JWT(روش جدید)خودJWT یک استاندارد وب (RFC 7519) و یک روش ارسال اطلاعات مبتنی بر توکن بین دو محل (در اینجا سرور و کلاینت) هست. اطلاعات به صورت json هستن و قبل از ارسال به سرور با الگوریتم base-64 انکد میشن. توجه کنید که تا اینجای کار هیچ مکانیزم انکریپشنی وجود نداره بنابراین تاکید می کنم که هرگز اطلاعات حساس رو با JWT ارسال نکنید(می تونید در مرحله لاگین از روش POST استفاده کنید.). بعد از اون سرور با استفاده از یه کلید (secret key) اطلاعات رو با الگوریتم خاصی (HMAC یا RSA) انکریپت می کنه و دوباره به سمت کاربر می فرسته و بعد از اون برنامه سمت کاربر وظیفه داره برای احراز هویت تو صفحات بعدی توکن انکریپت شده ارسالی توسط سرور رو در حافظه لوکالش ذخیره کنه. البته می تونه کوکی رو به جای لوکال به عنوان محل ذخیره سازی توکن انتخاب کنه.برای همه درخواست های بعدی از این به بعد فقط کافیه توکن از سمت کاربر دوباره به سمت سرور ارسال و صحتش از طریق بررسی کلید احراز بشه. دقت کنید که دیگه احتیاجی به بررسی دوباره دیتا بیس نیست. به همین دلیل JWT یک روش خود شمول محسوب میشه.مکانیزم کلی روش JWT رو می تونید تو عکس پایین ببینید: ساختار JWTدرJWT ها در شکل متراکم خود متشکل از 3 بخش هستند که با نقطه (.) از یکدیگر جدا شده‌اند:هدر (header)ارزش (payload)امضا (signature)بنابراین یک JWT نوعی به شکل زیر در می‌آید:xxxxx.yyyyy.zzzzzحالا بیاید به‌صورت جداگانه به هر بخش بپردازیم.هدراین بخش معمولاً متشکل از دو بخشِ نوع توکن و الگوریتم درهم سازی (hashing) است که در اینجا نوع توکن JWT و الگوریتم درهم سازی نیز HMAC SHA256 یا RSA است.برای مثال: { "alg": "HS256", "typ": "JWT" } سپس به‌صورت Base64Url کدگذاری می‌شود تا بخش اول JWT را شکل دهد.payloadبخش دوم توکن payload است که حاوی ادعاها و درخواست‌ها است. این درخواست‌ها دستوراتی در رابطه با یک نهاد (عموماً کاربر) و اطلاعات اضافی هستند. درخواست‌ها ه سه دسته تقسیم می‌شوند: درخواست‌های ثبت شده، عمومی و خصوصی.درخواست‌های ثبت شده: این درخواست‌ها، درخواست‌های از پیش تعریف‌شده‌ای هستند که الزامی نیستند، اما جهت ارائه‌ی مجموعه‌ای از درخواست‌های مفید و دارای قابلیت انتقال اطلاعات توصیه شده‌اند. از جمله‌ی آن‌ها می‌توان به iss (صادرکننده)، exp (زمان انقضا)، sub (موضوع)، aud (حضار) و ... اشاره کرد.توجه داشته باشید که اسم درخواست‌ها تنها از سه حرف تشکیل شده‌اند، چرا که JWT باید فشرده باشد.درخواست‌های عمومی: این درخواست‌ها را می‌توان از طریق درخواست‌هایی که از JWT ها استفاده می‌کنند، تعریف کرد. اما برای آن که از تلاقی بین آن‌ها جلوگیری شود، باید آن‌ها را در IANA JSON Web Token Registry تعریف کرد یا این‌که آن‌ها را به‌صورت یک URI تعریف کرد؛ به‌گونه‌ای که این URI شامل یک فضای نام مقاوم در برابر تلاقی باشد.درخواست‌های خصوصی: این درخواست‌ها، درخواست‌هایی اختصاصی هستند که از طریق آن‌ها می‌توان اطلاعات را بین طرفینی که بر سر استفاده از آن‌ها با یکدیگر توافق کرده‌اند و جزء درخواست‌های عمومی یا ثبت شده نیستند، به اشتراک گذاشت.در زیر یک payload نمونه را مشاهده می‌کنید:{ "sub": "1234567890", "name": "John Doe", "admin": true }بنابراین در اینجا payload ،Base64Url است که به‌گونه‌ای کدگذاری شده است که می‌تواند بخش دوم JWT را ایجاد کند.حتماً توجه داشته باشید که همه‌ی افراد می‌توانند این اطلاعات را در توکن های امضا شده بخوانند. هرچند که این اطلاعات در برابر تحریف مقاوم باشند. اطلاعات محرمانه‌ی خود را به‌هیچ‌وجه داخل عناصر هدر یا payload نگذارید، مگر آن که این اطلاعات رمزگذاری شده باشند.امضابرای آن که بتوانید بخش امضا را ایجاد کنید، باید هدر رمزگذاری شده، payload رمزگذاری شده، اطلاعات سری و الگوریتم مشخص‌شده در هدر را بگیرید و آن را امضا کنید.برای مثال اگر می‌خواهید از الگوریتم HMAC SHA256 استفاده کنید، امضا به‌صورت زیر ایجاد می‌شود:HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload, secret)این امضا در تأیید عدم تغییر پیام در امتداد مسیر کاربرد دارد و اگر توکن ها همراه با یک کلید خصوصی امضا شده باشند، در این صورت این امضا می‌تواند هویت واقعی فرستنده‌ی JWT را تأیید کند.ترکیب موارد بالا در کنار یکدیگرخروجی نهایی سه رشته‌ی Base64-URL است که با نقطه‌هایی از یکدیگر جدا شده‌اند و می‌توان آن‌ها را به‌راحتی در محیط‌های HTML و HTTP عبور داد. در عین حال این رشته‌ها در مقایسه با استانداردهای مبتنی بر XML مثل SAML فشرده‌تر هستند.در ادامه یک JWT را مشاهده می‌کنید که دارای هدر و payload رمزگذاری شده است و همراه با اطلاعات سری امضا شده است.اگر می‌خواهید که با JWT بازی کنید و تمامی این مفاهیم را در عمل مشاهده کنید، می‌توانید برای کدبرداری، تأیید و تولید JWT ها از اشکال‌یاب jwt.io استفاده کنید. اگر می‌خواهید که با JWT بازی کنید و تمامی این مفاهیم را در عمل مشاهده کنید، می‌توانید برای کدبرداری، تأیید و تولید JWT ها از اشکال‌یاب jwt.io استفاده کنید نرم افزار Xamppیک شبیه ساز سرور به صورت لوکال می‌باشد که بسیاری از مشکلات برنامه نویسان و طراحان وب را حل کرده است و به آن ها اجازه می‌دهد که بدون نیاز به دسترسی به اینترنت کارهای خود را روی کامپیوتر خود آزمایش کنند. همچنین سرویس دهنده‌های Apache و MySQL و Perl و PHP نیز به صورت اتوماتیک در هنگام نصب نرم‌افزار Xampp به سادگی نصب و پیکربندی می‌شوند. در واقع نرم‌‌افزار Xampp کدهای php را به صورت localhost در سیستم شما اجرا می‌کند و با این کار می‌توانید قبل از آپلود کردن کارهای خود روی هاست اصلی‚ آن های را روی کامپیوتر شخصی خود تست کنید. خیلی خب احراز هویت را بیان کردیم حالا بپردازیم به اصل کار و پیاده سازی ابتدای کار باید پایگاه داده رو ایجاد کنیم:پایگاه داده مورد استفاده ما MySql هست یه جدول ایجاد میکنیم من اسمشو edmtdb میزارم بعدش یه جدول به اسم users ایجاد میکنیم و ستون هایunique_id , name , email , encrypted_password , salt , created_atایجاد میکنیم این کار رو توی محیط xampp ایجاد کردم.حالا بعدش به سراغ php میریمیه فایل config.php ایجاد میکنیم که حاوی اطلاعات اتصال به پایگاه داده هست<?php define("DB_HOST","localhost"); define("DB_USER","root"); define("DB_PASSWORD",""); define("DB_DATABASE","edmtdb"); ?>حالا یه فایل دیگه به نام db_connect.php ایحاد میکنیم تا به پایگاه داده وصل بشیم<?php class DB_CONNECT{ private $conn; public function connect(){ require_once 'config.php'; $this->conn=new mysqli(DB_HOST,DB_USER,DB_PASSWORD,DB_DATABASE); return $this->conn; } } ?>حالا برای عملیاتی که انجام باید بدیم باید یه سری توابع بنویسیم تا بتونیم کار هایی رو که باید انجام بدیم رو پیاده سازی کنیم یه فایل دیگه ایجاد می کنیم اسمش رو میزاریم db_function.php <?php class DB_Functions{ private $conn; function __construct(){ require_once 'db_connect.php'; $db=new DB_CONNECT(); $this->conn=$db->connect(); } function __destruct(){} public function storeUser($name,$email,$password){ $uuid = HMACSHA256(base64UrlEncode($name) + "." +base64UrlEncode($password),secret); $hash=$this->hashSSHA($password); $encrypted_password=$hash['encrypted']; $salt=$hash["salt"]; $stmt=$this->conn->prepare("INSERT INTO users(unique_id,name,email,encrypted_password,salt,created_at) VALUES (?,?,?,?,?,NOW())"); $stmt->bind_param("sssss",$uuid,$name,$email,$encrypted_password,$salt); $result=$stmt->execute(); $stmt->close(); if($result) { $stmt=$this->conn->prepare("SELECT * FROM users WHERE email=?"); $stmt->bind_param("s",$email); $stmt->execute(); $user=$stmt->get_result()->fetch_assoc(); $stmt->close(); return $user; } else { return false; } } public function getUserByEmailAndPassword($email,$password){ $stmt=$this->conn->prepare("SELECT * FROM users WHERE email=?"); $stmt->bind_param("s",$email); if($stmt->execute()) { $user=$stmt->get_result()->fetch_assoc(); $stmt->close(); $salt=$user['salt']; $encrypted_password=$user['encrypted_password']; $hash=$this->checkhashSSHA($salt,$password); if($encrypted_password==$hash) { return $user; } else{ return NULL; } } } public function isUserExisted($email){ $stmt=$this->conn->prepare("SELECT email FROM users WHERE email=?"); $stmt->bind_param("s",$email); $stmt->execute(); $stmt->store_result(); if($stmt->num_rows>0) { $stmt->close(); return true; } else{ $stmt->close(); return false; } } public function hashSSHA($password){ $salt=sha1(rand()); $salt=substr($salt,0,10); $encrypted=base64_encode(sha1($password . $salt,true).$salt); $hash=array("salt"=>$salt,"encrypted"=>$encrypted); return $hash;حالا میرسیم به جایی که همه فکرشو میکنن که صفحه Login هست حالا یه فایل login.php ایجاد میکنیم<?php require_once 'db_function.php'; $db=new DB_Functions(); //json response $response=array(); if(isset($_POST['email'])&&isset($_POST['password'])) { $email=$_POST['email']; $password=$_POST['password']; $user=$db->getUserByEmailAndPassword($email,$password); if($user!=false) { $response ["error"]= false; $response ["uuid"]=$user["unique_id"]; $response ["user"]["name"]=$user["name"]; $response ["user"]["email"]=$user["email"]; $response ["user"]["created_at"]=$user["created_at"]; $response ["user"]["updated_at"]=$user["updated_at"]; echo json_encode($response); } else{ $response ["error"]= true; $response ["error_msg"]="Login information are wrong. Please try again! ".$email; echo json_encode($response); } } else { $response ["error"]= true; $response ["error_msg"]="Requied parameters (name,email,password) is missing !".$email; echo json_encode($response); } ?>بعدش حالا یه فایل دیگه برای Register کردن یا SignUp کردن ایجاد میکنیم به اسم Register.php ایجاد میکنیم<?php require_once 'db_function.php'; $db=new DB_Functions(); //json response $response=array("error"=>FALSE); if(isset($_POST['name'])&&isset($_POST['email'])&&isset($_POST['password'])) { $name=$_POST['name']; $email=$_POST['email']; $password=$_POST['password']; if($db->isUserExisted($email)) { $response ["error"]=TRUE; $response ["error_msg"]="User already existed with ".$email; echo json_encode($response); } else{ $user=$db->storeUser($name,$email,$password) if($user) { $response ["error"]=FALSE; $response ["uuid"]=$user["unique_id"]; $response ["user"]["name"]=$user["name"]; $response ["user"]["email"]=$user["email"]; $response ["user"]["created_at"]=$user["created_at"]; $response ["user"]["updated_at"]=$user["updated_at"]; echo json_encode($response); } else{ $response ["error"]=TRUE; $response ["error_msg"]="Unkown error occured in registration !".$email; echo json_encode($response); } } } else { $response ["error"]=TRUE; $response ["error_msg"]="Requied parameters (name,email,password) is missing !".$email; echo json_encode($response); } ?>خب تا اینجا بخش php رو تموم کردیم دقت کنین همه این فایل ها باید در یه پوشه باشن تا درست کار کننبه سراغ بخش اندروید بریم من پروژه رو آپلود کردم و برای دانلود کلیک کنید.حالا بپردازیم به بخش اندروید در ابتدا به بخش ui اندروید بریم که همون طور که میدونیم از فرمت .xml پشتیبانی میکنه کدش زیاد پیچیده نیس فقط باید کار با layout ها و کامپوننت ها رو بلد باشین که کد بخش login به صورت زیر است:<?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:background="@drawable/auth_pic" android:orientation="vertical" tools:context=".MainActivity"> <ScrollView android:layout_height="match_parent" android:layout_width="match_parent" android:id="@+id/login_form" android:background="@drawable/auth_pic"> <LinearLayout android:layout_width="match_parent" android:id="@+id/email_login_form" android:orientation="vertical" android:layout_height="wrap_content"> <View android:layout_width="match_parent" android:layout_height="200dp" android:id="@+id/logo_here"/> <LinearLayout android:layout_marginLeft="20dp" android:layout_marginBottom="20dp" android:layout_marginRight="20dp" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:orientation="horizontal" android:layout_marginBottom="20dp" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="50dp" android:layout_height="60dp" android:background="@drawable/blue_blur" android:gravity="center_vertical|center_horizontal" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/blue_blur" android:orientation="vertical"> <ImageView android:layout_marginLeft="7dp" android:layout_width="35dp" android:layout_height="25dp" android:scaleType="fitCenter" android:src="@drawable/email" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/white_right" android:orientation="vertical"> <EditText android:id="@+id/ed_email_m" android:layout_width="match_parent" android:layout_height="60dp" android:background="@android:color/transparent" android:hint="Email" android:layout_marginLeft="15dp" android:inputType="textEmailAddress" android:textSize="16dp" /> </LinearLayout> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="50dp" android:layout_height="60dp" android:background="@drawable/blue_blur" android:gravity="center_vertical|center_horizontal" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/blue_blur" android:orientation="vertical"> <ImageView android:layout_width="50dp" android:layout_height="40dp" android:scaleType="fitCenter" android:src="@drawable/password" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/white_right" android:orientation="vertical"> <EditText android:id="@+id/ed_pass_m" android:layout_width="match_parent" android:layout_height="60dp" android:background="@android:color/transparent" android:hint="Password" android:layout_marginLeft="15dp" android:inputType="textEmailAddress" android:textSize="16dp" /> </LinearLayout> </LinearLayout> <Button android:layout_marginTop="30dp" android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="20sp" android:textColor="#fff" android:id="@+id/btn_login" android:text="LOGIN" android:background="@drawable/button_blur" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center" android:id="@+id/txt_reg" android:textColor="#fff" android:textSize="16dp" android:text="Dont have Account! SignUp"/> </LinearLayout> </LinearLayout> </ScrollView> </LinearLayout>بعد طراحی ui کار حالا به سراغ استفاده از این ui میریم که توی اندروید به دو صورت جاوا یا جدیدا از زبان کاتلین استفاده میشه که من از جاوا استفاده کردم که کدش به صورت زیر هستش:package com.example.androidauthphp; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.example.androidauthphp.Model.APIResponse; import com.example.androidauthphp.Remote.IMYAPI; import com.example.androidauthphp.common.Common; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class MainActivity extends AppCompatActivity { TextView txt_reg; EditText edt_pass,edt_em; Button btn_log; IMYAPI mserv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mserv= Common.getAPI(); txt_reg=(TextView) findViewById(R.id.txt_reg); edt_em=(EditText)findViewById(R.id.ed_email_m); edt_pass=(EditText)findViewById(R.id.ed_pass_m); btn_log=(Button)findViewById(R.id.btn_login); txt_reg.setListener(new View.Listener() { @Override public void (View view) { startActivity(new Intent(MainActivity.this,RegisterActivity.class)); } }); btn_log.setListener(new View.Listener() { @Override public void (View view) { authenticateUser(edt_em.getText().toString(),edt_pass.getText().toString()); } }); } private void authenticateUser (String email,String pass) { mserv.loginUser(email,pass) .enqueue(new Callback<APIResponse>() { @Override public void onResponse(Call<APIResponse> call, Response<APIResponse> response) { APIResponse result=response.body(); if(result.isError()) Toast.makeText(MainActivity.this,result.getError_msg(), Toast.LENGTH_SHORT).show(); else { Toast.makeText(MainActivity.this, "Login Success !", Toast.LENGTH_SHORT).show(); startActivity(new Intent(MainActivity.this, StartActivity.class)); } } @Override public void onFailure(Call<APIResponse> call, Throwable t) { } }); } }بعد اینکه بخش login رو تموم کردیم به سراغ بخش register میریم که کدش به صورت زیر هستش که بازم از ui شروع میکنیم<?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:background="@drawable/auth_pic" android:orientation="vertical" tools:context=".RegisterActivity"> <ScrollView android:layout_height="match_parent" android:layout_width="match_parent" android:id="@+id/login_form" android:background="@drawable/auth_pic"> <LinearLayout android:layout_width="match_parent" android:id="@+id/email_login_form" android:orientation="vertical" android:layout_height="wrap_content"> <View android:layout_width="match_parent" android:layout_height="200dp" android:id="@+id/logo_here"/> <LinearLayout android:layout_marginLeft="20dp" android:layout_marginBottom="20dp" android:layout_marginRight="20dp" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:orientation="horizontal" android:layout_marginBottom="20dp" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="50dp" android:layout_height="60dp" android:background="@drawable/blue_blur" android:gravity="center_vertical|center_horizontal" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/blue_blur" android:orientation="vertical"> <ImageView android:layout_width="50dp" android:layout_height="40dp" android:scaleType="fitCenter" android:src="@drawable/email" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/white_right" android:orientation="vertical"> <EditText android:id="@+id/ed_email" android:layout_width="match_parent" android:layout_height="60dp" android:background="@android:color/transparent" android:hint="Email" android:layout_marginLeft="15dp" android:inputType="textEmailAddress" android:textSize="16dp" /> </LinearLayout> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_marginBottom="20dp" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="50dp" android:layout_height="60dp" android:background="@drawable/blue_blur" android:gravity="center_vertical|center_horizontal" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/blue_blur" android:orientation="vertical"> <ImageView android:layout_width="50dp" android:layout_height="40dp" android:scaleType="fitCenter" android:src="@drawable/user" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/white_right" android:orientation="vertical"> <EditText android:id="@+id/ed_nm" android:layout_width="match_parent" android:layout_height="60dp" android:background="@android:color/transparent" android:hint="Name" android:layout_marginLeft="15dp" android:inputType="textEmailAddress" android:textSize="16dp" /> </LinearLayout> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="50dp" android:layout_height="60dp" android:background="@drawable/blue_blur" android:gravity="center_vertical|center_horizontal" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/blue_blur" android:orientation="vertical"> <ImageView android:layout_width="50dp" android:layout_height="40dp" android:scaleType="fitCenter" android:src="@drawable/password" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/white_right" android:orientation="vertical"> <EditText android:id="@+id/ed_pass" android:layout_width="match_parent" android:layout_height="60dp" android:background="@android:color/transparent" android:hint="Password" android:layout_marginLeft="15dp" android:inputType="textEmailAddress" android:textSize="16dp" /> </LinearLayout> </LinearLayout> <Button android:layout_marginTop="30dp" android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="20sp" android:id="@+id/btn_reg" android:textColor="#fff" android:text="REGISTER" android:background="@drawable/button_blur" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center" android:textColor="#fff" android:id="@+id/txt_sin" android:textSize="16dp" android:text="I have Account! SignIn"/> </LinearLayout> </LinearLayout> </ScrollView> </LinearLayout>حالا به بخش جاوا بریم :package com.example.androidauthphp; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.example.androidauthphp.Model.APIResponse; import com.example.androidauthphp.Remote.IMYAPI; import com.example.androidauthphp.common.Common; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class RegisterActivity extends AppCompatActivity { TextView txt_sin; EditText edt_pass,edt_em,ed_nm; Button btn_reg; IMYAPI mserv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_register); mserv= Common.getAPI(); txt_sin=findViewById(R.id.txt_sin); edt_em=findViewById(R.id.ed_email); ed_nm=findViewById(R.id.ed_nm); edt_pass=findViewById(R.id.ed_pass); btn_reg=findViewById(R.id.btn_reg); txt_sin.setListener(new View.Listener() { @Override public void (View view) { finish(); } }); btn_reg.setListener(new View.Listener() { @Override public void (View view) { createNewUsers(ed_nm.getText().toString(),edt_em.getText().toString(),edt_pass.getText().toString()); } }); } private void createNewUsers(String name, String email, String password) { mserv.registerUser(name,email,password) .enqueue(new Callback<APIResponse>() { @Override public void onResponse(Call<APIResponse> call, Response<APIResponse> response) { APIResponse result=response.body(); if(result.isError()) Toast.makeText(RegisterActivity.this,result.getError_msg(), Toast.LENGTH_SHORT).show(); else Toast.makeText(RegisterActivity.this, "User created : !"+ result.getUid(), Toast.LENGTH_SHORT).show(); } @Override public void onFailure(Call<APIResponse> call, Throwable t) { Toast.makeText(RegisterActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show(); } }); } }بعدش یه صفحه ساده هست که توی عکس ها پایین خواهد دید که کد هاش به صورت زیر هست:<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout 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:background="@drawable/auth_pic" android:layout_height="match_parent" tools:context=".StartActivity"> <Button android:id="@+id/btn_h" android:layout_width="200dp" android:layout_height="100dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:background="@drawable/button_blur" android:text="Hello" android:textColor="#fff" android:textSize="30sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:ignore="MissingConstraints" /> <TextView android:id="@+id/txt_h" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:gravity="center" android:text="Logout" android:textColor="#fff" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn_h" app:layout_constraintVertical_bias="0.349" tools:ignore="MissingConstraints" tools:layout_editor_absoluteX="0dp" /> </android.support.constraint.ConstraintLayout>package com.example.androidauthphp; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.TextView; import android.widget.Toast; public class StartActivity extends AppCompatActivity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_start); textView=findViewById(R.id.txt_h); textView.setListener(new View.Listener() { @Override public void (View view) { Toast.makeText(StartActivity.this, "Logout", Toast.LENGTH_SHORT).show(); startActivity(new Intent(StartActivity.this,MainActivity.class)); } }); } }من برای ارتباط با بخش php از کتابخونه retrofit استفاده کردم که برای این کار باید یه سری کلاس ها رو اضافه کنیم :کلاس commonpackage com.example.androidauthphp.common; import com.example.androidauthphp.Remote.IMYAPI; import com.example.androidauthphp.Remote.RetrofitClient; public class Common { public static final String BASE_URL="http://10.0.2.2/myapi/"; public static IMYAPI getAPI() { return RetrofitClient.getClient(BASE_URL).create(IMYAPI.class); } }کلاس apiresponse package com.example.androidauthphp.Model; public class APIResponse { private boolean error; private String uid; private User user; private String error_msg; public APIResponse() { } public boolean isError() { return error; } public void setError(boolean error) { this.error = error; } public String getUid() { return uid; } public void setUid(String uid) { this.uid = uid; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getError_msg() { return error_msg; } public void setError_msg(String error_msg) { this.error_msg = error_msg; } }کلاس userpackage com.example.androidauthphp.Model; public class User { private String name; private String email; private String created_at; private String updated_at=""; public User(String name, String email, String created_at, String updated_at) { this.name = name; this.email = email; this.created_at = created_at; this.updated_at = updated_at; } public User() { } public String getName() { return name; } public String getEmail() { return email; } public String getCreated_at() { return created_at; } public String getUpdated_at() { return updated_at; } }یه رابط برای ارتباط با بخش وب به نام IMYAPI:package com.example.androidauthphp.Remote; import com.example.androidauthphp.Model.APIResponse; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Field; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.POST; public interface IMYAPI { @FormUrlEncoded @POST("Login.php") Call<APIResponse> loginUser(@Field("email") String email, @Field("password") String password); @FormUrlEncoded @POST("Register.php") Call<APIResponse> registerUser(@Field("name") String name,@Field("email") String email, @Field("password") String password); @GET("test.php") Call<ResponseBody> test(); }و رد پایان کلاس RetrofitClient :package com.example.androidauthphp.Remote; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class RetrofitClient { private static Retrofit retrofit; public static synchronized Retrofit getClient(String baseUrl) { if(retrofit==null) { retrofit=new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build(); } return retrofit; } }در پایان هم یه سر اسکرین شات از برنامه قرار میدم :راستی تا یادم نرفته یه سری کتابخونه توی اندروید استفاده کردم اینم بزارم:implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' //testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'com.squareup.retrofit2:retrofit:2.3.0' implementation 'com.squareup.retrofit2:converter-gson:2.3.0'اینم یه سری عکس از محیط برنامه ای که آپلود کردمحالا تنها کاری که لازمه برای استفاده از پروژه اینه که نرم افزار اندروید استودیو داشته باشین ورژنی که من استفاده کردم 3.3.2 بود و یه نرم افزار xampp و یه ویرایش گر برای فایل های php و همین طور ساخت پایگاه داده و نکته مهم آخر برای اجرای نرم افزار اندروید از امولیتور خود اندروید استودیو استفاده کنین چون من ip رو توی پروژه اندروید روی اونا تنظیم کردم اگه خواستین روی گوشی اجرا کنین باید ip گوشی تون رو بدین و از یه طریق گوشی و سیستم رو به یه شبکه مثله وای فای یا هات اسپات گوشی وصل کنین در پایان اگه مشکلی بود به من ایمیل بزنین :mortezaaaf@hotmail.com و همین طور تشکر میکنم از استادم که در انجام این پروژه کمکم کردن آقای مهندس محمد نصیری .با تشکر مرتضی فائزی نیا دانشجوی مهندسی نرم افزار ، برنامه نویس اندروید مطالبی از لینک زیر استفاده شده است : https://arsess-co.com/%D8%A7%D8%AD%D8%B1%D8%A7%D8%B2-%D9%87%D9%88%DB%8C%D8%AA-%DB%8C%D8%A7-authentication-%DA%86%DB%8C%D8%B3%D8%AA-%D8%9F/ http://www.tahlildadeh.com/ArticleDetails/JWT-JSON-Web-Tokens http://duckylearn.com/%D9%87%D9%85%D9%87-%DA%86%DB%8C%D8%B2-%D8%AF%D8%B1-%D9%85%D9%88%D8%B1%D8%AF-jwt/