در بسیاری از اپلیکیشنهای لاراولی، نیاز است تا احراز هویت کاربران را با استفاده از تلفن همراه و ارسال اس ام اس در لاراول انجام دهیم. در بسیاری از اپلیکیشنهای موجود، برای ورود کاربر، علاوه بر گزینهی ورود با ایمیل و رمز عبور، گزینهی ورود به وسیلهی ارسال کد تایید تلفن همراه نیز وجود دارد. در این مقاله، قصد داریم نحوهی ایجاد این ویژگی را در قالب یک مثال عملی و به صورت کاربردی برای شما تشریح کنیم. پس تا انتهای مقاله با ما همراه باشید تا با نحوهی احراز هویت با موبایل در لاراول آشنا شوید.
اگر تو هم به فریم ورک لاراول علاقهمندی و دوست داری در این زمینه حرفه ای بشی و کسب درآمد کنی پیشنهاد میکنیم در دوره آموزش لاراول سون لرن شرکت کنی تا از مزایا یکسال پشتیبانی دوره بهرهمند بشی.
برای شروع آموزش احراز هویت پیامکی با موبایل در لاراول، به دانش کافی در زمینهی لاراول نیاز دارید.
به پوشهی دلخواه خود بروید و دستور زیر را برای نصب لاراول اجرا کنید:
composer create-project --prefer-dist laravel/laravel blog
با دستور زیر میتوانید پروژه را اجرا کنید:
php artisan serve
پس از اجرای این دستور، پروژه بر روی پورت 8000 لوکال هاست در دسترس است. با وارد کردن آدرس http://localhost:8000 در نوار آدرس مرورگر، با این صفحه مواجه میشوید:
قدم بعدی، برقراری ارتباط با دیتابیس است. ابتدا یک دیتابیس دلخواه را ایجاد کنید و سپس مقادیر متغیرهای زیر را در فایل env. پروژه بهروز کنید:
DB_DATABASE=mobile_auth
DB_USERNAME=root
DB_PASSWORD=
نیاز است تا فایل Migration پیشفرض مربوط به جدول کاربران را به صورت زیر تغییر دهید:
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->string('phone')->nullable();
$table->rememberToken();
$table->timestamps();
});
}
با استفاده از دستور زیر مدل و Migration مربوط به جدول توکنها را ایجاد کنید:
php artisan make:model Token -m
فایل Migration مربوطه را به صورت زیر ویرایش کنید:
Schema::create('tokens', function (Blueprint $table) {
$table->increments('id');
$table->string('code', 4);
$table->integer('user_id')->unsigned();
$table->boolean('used')->default(false);
$table->timestamps();
});
اکنون با استفاده دستور زیر جداول را ایجاد کنید:
php artisan migrate
در اینجا میخواهیم مدلها را بهروزرسانی و متدهایی را به آنها اضافه کنیم.
مدل User را به شکل زیر بهروز کنید:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'email',
'password',
'phone'
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* User tokens relation
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function tokens()
{
return $this->hasMany(Token::class);
}
}
در کد بالا مشاهده میکنید که رابطهی hasMany را بین جدول کاربران و جدول توکنها تعریف کردهایم.
برای مدل توکن، متدهای مختلفی را تعریف میکنیم:
<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Token extends Model
{
use HasFactory;
const EXPIRATION_TIME = 15; // minutes
protected $fillable = [
'code',
'user_id',
'used'
];
public function __construct(array $attributes = [])
{
if (! isset($attributes['code'])) {
$attributes['code'] = $this->generateCode();
}
parent::__construct($attributes);
}
/**
* Generate a six digits code
*
* @param int $codeLength
* @return string
*/
public function generateCode($codeLength = 4)
{
$max = pow(10, $codeLength);
$min = $max / 10 - 1;
$code = mt_rand($min, $max);
return $code;
}
/**
* User tokens relation
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* True if the token is not used nor expired
*
* @return bool
*/
public function isValid()
{
return ! $this->isUsed() && ! $this->isExpired();
}
/**
* Is the current token used
*
* @return bool
*/
public function isUsed()
{
return $this->used;
}
/**
* Is the current token expired
*
* @return bool
*/
public function isExpired()
{
return $this->created_at->diffInMinutes(Carbon::now()) > static::EXPIRATION_TIME;
}
public function sendCode()
{
if (! $this->user) {
throw new \Exception("No user attached to this token.");
}
if (! $this->code) {
$this->code = $this->generateCode();
}
try {
// send code via SMS
} catch (\Exception $ex) {
return false; //enable to send SMS
}
return true;
}
}
در کد بالا مشاهده میکنید که متد ()generateCode را برای ایجاد اعداد تصادفی، متد ()isValid را برای بررسی اعتبار کد، متد ()isUsed را برای بررسی کد استفاده شده و متد ()isExpired را برای بررسی انقضای کد تعریف کردهایم. همچنین ثابت EXPIRATION_TIME را به عنوان مدت انقضای کد در واحد دقیقه تعریف نمودهایم.
متد ()sendCode را برای ارسال کد تایید برای کاربر تعریف کردهایم. در این متد، ابتدا در صورت عدم وجود رابطهی ()user، یک Exception را Throw کردهایم. سپس، در صورت عدم ایجاد کد که در متد ()construct__ تعریف کردهایم، یک کد جدید را ایجاد میکنیم. درون بلاک try، بسته به سرویس پیامکی دلخواهی که استفاده میکنید، کد ارسال پیامک سرویس موردنظر را وارد کنید. در صورت بروز هرگونه مشکل نیز، اخطار به وجود آمده را در بلاک ()catch مدیریت کنید.
اکنون نیاز داریم قالب Blade صفحات موردنیازمان را ایجاد کنیم.
برای صفحهی ثبت نام از قالب زیر استفاده میکنیم:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Register</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
</head>
<body>
<div id="login">
<h3 class="text-center text-white pt-5">Register form</h3>
<div class="container">
<div id="login-row" class="row justify-content-center align-items-center">
<div id="login-column" class="col-md-6">
<div id="login-box" class="col-md-12">
<form id="login-form" class="form" action="{{ route('doRegister') }}" method="post">
{{ csrf_field() }}
<h3 class="text-center text-info">Register</h3>
<div class="form-group">
<label for="name" class="text-info">Name:</label><br>
<input type="text" name="name" id="name" class="form-control">
</div>
<div class="form-group">
<label for="email" class="text-info">Email:</label><br>
<input type="email" name="email" id="email" class="form-control">
</div>
<div class="form-group">
<label for="password" class="text-info">Password:</label><br>
<input type="password" name="password" id="password" class="form-control">
</div>
<div class="form-group">
<label for="password_confirmation" class="text-info">Confirm Password:</label><br>
<input type="password" name="password_confirmation" id="password_confirmation" class="form-control">
</div>
<div class="form-group">
<label for="phone" class="text-info">Phone Number:</label><br>
<input type="text" name="phone" id="phone" class="form-control">
</div>
<div class="form-group">
<input type="submit" name="submit" class="btn btn-info btn-md" value="submit">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</body>
</html>
برای صفحهی ورود با ایمیل از قالب زیر استفاده میکنیم:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Login</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
</head>
<body>
<div id="login">
<h3 class="text-center text-white pt-5">Login form</h3>
<div class="container">
<div id="login-row" class="row justify-content-center align-items-center">
<div id="login-column" class="col-md-6">
<div id="login-box" class="col-md-12">
<form id="login-form" class="form" action="{{ route('doLoginEmail') }}" method="post">
{{ csrf_field() }}
<h3 class="text-center text-info">Login</h3>
<div class="form-group">
<label for="email" class="text-info">Email:</label><br>
<input type="email" name="email" id="email" class="form-control">
</div>
<div class="form-group">
<label for="password" class="text-info">Password:</label><br>
<input type="password" name="password" id="password" class="form-control">
</div>
<div class="form-group">
<label for="remember_me" class="text-info"><span>Remember me</span> <span><input
id="remember_me" name="remember_me" type="checkbox"></span></label><br>
<input type="submit" name="submit" class="btn btn-info btn-md" value="submit">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</body>
</html>
برای صفحهی ورود با تلفن از قالب زیر استفاده میکنیم:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Login</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
</head>
<body>
<div id="login">
<h3 class="text-center text-white pt-5">Login form</h3>
<div class="container">
<div id="login-row" class="row justify-content-center align-items-center">
<div id="login-column" class="col-md-6">
<div id="login-box" class="col-md-12">
<form id="login-form" class="form" action="{{ route('doLoginPhone') }}" method="post">
{{ csrf_field() }}
<h3 class="text-center text-info">Login</h3>
<div class="form-group">
<label for="phone" class="text-info">Phone Number:</label><br>
<input type="text" name="phone" id="phone" class="form-control">
</div>
<div class="form-group">
<label for="remember_me" class="text-info"><span>Remember me</span> <span><input
id="remember_me" name="remember_me" type="checkbox"></span></label><br>
<input type="submit" name="submit" class="btn btn-info btn-md" value="submit">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</body>
</html>
قالب زیر را نیز برای صفحهی تایید کد ارسالی استفاده میکنیم:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Login</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
</head>
<body>
<div id="login">
<h3 class="text-center text-white pt-5">Login form</h3>
<div class="container">
<div id="login-row" class="row justify-content-center align-items-center">
<div id="login-column" class="col-md-6">
<div id="login-box" class="col-md-12">
<form id="login-form" class="form" action="{{ route('doVerify') }}" method="post">
{{ csrf_field() }}
<h3 class="text-center text-info">Login</h3>
<div class="form-group">
<label for="code" class="text-info">Verification Code:</label><br>
<input type="text" name="code" id="code" class="form-control">
</div>
<div class="form-group">
<input type="submit" name="submit" class="btn btn-info btn-md" value="submit">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</body>
</html>
در مسیر public/css یک فایل CSS با نام style.css ایجاد کنید و کدهای زیر را در آن کپی کنید:
body {
margin: 0;
padding: 0;
background-color: #17a2b8;
height: 100vh;
}
#login .container #login-row #login-column #login-box {
margin: 50px auto;
max-width: 600px;
height: auto;
border: 1px solid #9C9C9C;
background-color: #EAEAEA;
}
#login .container #login-row #login-column #login-box #login-form {
padding: 20px;
}
#login .container #login-row #login-column #login-box #login-form #register-link {
margin-top: -85px;
}
با استفاده از دستور زیر کنترلر AuthController را ایجاد میکنیم:
php artisan make:controller AuthController
برای ثبت نام از متدهای register و doRegister استفاده میکنیم. همانطور که میبینید در اعتبارسنجی دادههای ارسالی، ورود شمارهی موبایل را اختیاری قرار دادهایم:
public function register()
{
return view('auth.register');
}
public function doRegister(Request $request)
{
$data = $request->all();
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:users',
'password' => 'required|confirmed',
'phone' => 'nullable|numeric',
]);
$data['password'] = bcrypt($request->password);
$user = User::create($data);
auth()->login($user);
return redirect()->route('index');
}
public function loginPhone()
{
return view('auth.login-phone');
}
برای ورود با ایمیل از دو متد loginEmail و doLoginEmail استفاده میکنیم:
public function loginEmail()
{
return view('auth.login-email');
}
public function doLoginEmail(Request $request)
{
$this->validate($request, [
'email' => 'required',
'password' => 'required'
]);
$data = $request->only('email', 'password');
$rememberMe = $request->input('remember_me');
if (auth()->attempt($data, $rememberMe))
return redirect()->route('index');
else
return redirect()->back()->withErrors('Either email or password is wrong.');
}
برای ورود با موبایل از دو متد loginPhone و doLoginPhone استفاده میکنیم:
public function loginPhone()
{
return view('auth.login-phone');
}
public function doLoginPhone(Request $request)
{
$data = $request->all();
$this->validate($request, [
'phone' => 'required|exists:users',
]);
$user = User::where('phone', $request->input('phone'))->first();
$token = Token::create([
'user_id' => $user->id
]);
$rememberMe = ( !empty( $request->remember_me ) )? TRUE : FALSE;
if ($token->sendCode()) {
session()->put("code_id", $token->id);
session()->put("user_id", $user->id);
session()->put("remember", $rememberMe);
return redirect()->route('verify');
}
$token->delete();
return redirect()->route('login')->withErrors([
"Unable to send verification code"
]);
}
همانطور که میبینید، برای ارسال کد از متد ()sendCode موجود در مدل Token استفاده کردهایم. در صورت موفقیتآمیز بودن ارسال کد، سه مقدار شناسهی توکن ارسالی، شناسهی کاربر و مقدار فیلد Remember Me یا مرا به خاطر بسپار را در سشن ذخیره کردهایم. از این مقادیر در متد مربوط به تایید کد استفاده خواهیم کرد.
برای تایید کد ارسالی دو متد verify و doVerify را تعریف میکنیم. در اینجا، اعتبارسنجی شماره موبایل در لاراول را انجام میدهیم:
public function verify() {
return view('auth.verify');
}
public function doVerify(Request $request) {
$this->validate($request, [
'code' => 'required|numeric'
]);
if (!session()->has('code_id') || !session()->has('user_id'))
redirect()->route('loginPhone');
$token = Token::where('user_id', session()->get('user_id'))->find(session()- >get('code_id'));
if (!$token || empty($token->id))
redirect()->route('loginPhone');
if (!$token->isValid())
redirect()->back()->withErrors('The code is either expired or used.');
if ($token->code !== $request->input('code'))
redirect()->back()->withErrors('The code is wrong.');
$token->update([
'used' => true
]);
$user = User::find(session()->get('user_id'));
$rememberMe = session()->get('remember');
auth()->login($user, $rememberMe);
return redirect()->route('index');
}
همانطور که میبینید، پس از اعتبارسنجی، وجود مقادیر سشنها را بررسی کردهایم تا از ارسال درخواستهای متفرقه جلوگیری شود. در ادامه، با استفاده از مقادیر موجود در سشن، رکورد مربوط به کد را به دست میآوریم و سپس با استفاده از متد ()isValid موجود در مدل Token، آن را اعتبارسنجی میکنیم. سپس کد ارسال شده از سمت کاربر را با کد موجود در رکورد، مقایسه میکنیم و اعتبارسنجی شماره موبایل در لاراول را انجام میدهیم. در انتها نیز با استفاده از مقدار Remember Me موجود در سشن در متد ()login، کاربر را لاگین میکنیم. به این صورت میتوانیم لاگین با شماره موبایل در لاراول را عملی کنیم.
برای خروج هم از متد logout، به صورت زیر استفاده میکنیم:
public function logout()
{
auth()->logout();
return redirect()->back();
}
مسیرهای موردنیازمان را به شکل زیر تعریف میکنیم:
Route::get('/', function () {
return view('welcome');
})->name('index');
Route::get('/login-phone', [\App\Http\Controllers\AuthController::class, 'loginPhone'])->name('loginPhone');
Route::get('/login-email', [\App\Http\Controllers\AuthController::class, 'loginEmail'])->name('loginEmail');
Route::get('/register', [\App\Http\Controllers\AuthController::class, 'register'])->name('register');
Route::post('/register', [\App\Http\Controllers\AuthController::class, 'doRegister'])->name('doRegister');
Route::post('/login-phone', [\App\Http\Controllers\AuthController::class, 'doLoginPhone'])->name('doLoginPhone');
Route::post('/login-email', [\App\Http\Controllers\AuthController::class, 'doLoginEmail'])->name('doLoginEmail');
Route::get('/verify', [\App\Http\Controllers\AuthController::class, 'verify'])->name('verify');
Route::post('/doVerify', [\App\Http\Controllers\AuthController::class, 'doVerify'])->name('doVerify');
Route::get('/logout', [\App\Http\Controllers\AuthController::class, 'logout'])->name('logout');
اکنون پروژهی لاگین با شماره تلفن در لاراول آماده است و به عنوان مثال، با مراجعه به آدرس http://localhost:8000/register، با صفحهی زیر مواجه میشوید:
جمعبندی
در این مقاله از سری مقالات آموزش لاراول، با نحوهی احراز هویت با موبایل در لاراول آشنا شدیم و توانسیتم علاوه بر احراز هویت به وسیلهی ایمیل و رمز عبور، روش احراز هویت پیامکی در لاراول را نیز با مثال عملی بیاموزیم و نحوهی تایید شماره موبایل در لاراول را فرابگیریم. از الگوی معرفی شده در این مقاله، میتوانید به هر صورتی که نیاز داشتید استفاده کنید. میتوانید فقط از روش احراز هویت با SMS در لاراول استفاده کنید و از ایمیل و رمز عبور صرفنظر کنید. در این صورت، ستون Phone در جدول Users اجباری میشود و پس از ثبت نام نیز باید شمارهی همراه وارد شده توسط کاربر را با ارسال کد تایید پیامکی، اعتبارسنجی کنید.
امیدوار هستیم که این مقاله برای شما مفید بوده باشد. خوشحال میشویم نظرات، تجربیات و سوالات خود را با ما و سایر کاربران سون لرن به اشتراک بگذارید.
هرکس این آموزش رو گذاشته خدا امواتش رو بیامرزه
واقعا آفرین و دستتون درد نکنه
Big LIKE 10^