ایجاد صفحه احراز هویت سفارشی در لاراول — از صفر تا صد
در این مقاله، قصد داریم سیستم احراز هویت فریمورک لاراول را مورد بررسی قرار دهیم. هدف اصلی این مقاله ایجاد یک راهکار حفاظتی احراز هویت سفارشی در لاراول است که از طریق بسط سیستم احراز هویت مرکزی آن صورت میگیرد.
لاراول در هسته خود یک سیستم احراز هویت کاملاً پایدار دارد که پیادهسازی کارکرد احراز هویت را به سادگی امکانپذیر ساخته است. در واقع کافی است چند دستور آرتیزان اجرا کنید تا چارچوب یک سیستم احراز هویت آماده شود.
به علاوه خود سیستم طوری طراحی شده است که میتوان آن را بسط داد و به آداپترهای احراز هویت سفارشی نیز وصل کرد. این همان کاری است که قرار است در این مقاله انجام دهیم. پیش از آن که پا را فراتر گذارده و وارد جزییات پیادهسازی محافظ احراز هویت سفارشی شویم، کار خود را با بررسی عناصر ابتدایی سیستم احراز هویت آغاز میکنیم که محافظها و ارائه دهندهها هستند.
عناصر مرکزی: محافظها و ارائه دهندهها
سیستم احراز هویت سفارشی لاراول از دو عنصر اصلی به نام «محافظ» (Guard) و «ارائه دهنده» (Provider) تشکیل یافته است.
محافظ
محافظ را میتوان به چشم راهحل ارائه دهنده منطق برای شناسایی کاربران احراز هویت شده دید. لاراول در هسته مرکزی خود محافظهای مختلفی مانند «نشست» (Session) و «توکن» (Token) ارائه میکند. محافظ نشست، حالت کاربر را در هر درخواست به وسیله کوکی نگهداری میکند و در سوی دیگر محافظ توکن کاربر را از طریق یک توکن معتبر در هر درخواست احراز هویت میکند.
بنابراین چنان که میبینید محافظ، منطق احراز هویت را تعریف میکند و لزومی نیست که همیشه اقدام به بازیابی اطلاعات احراز هویت معتبر از بکاند بکند. شما میتوانید یک محافظ را که به سادگی حضور یک شیء خاص را در هدر درخواست بررسی میکند و کاربر را بر مبنای آن احراز هویت میکند پیادهسازی کنید.
در ادامه این مقاله ما یک محافظ پیادهسازی میکنیم که پارامترهای خاص JSON را در هدرهای درخواست بررسی میکند و کاربر معتبر را از بکاند بازیابی مینماید.
ارائه دهنده
اگر محافظ منطق احراز هویت را تعریف کند، ارائه دهنده احراز هویت مسئول بازیابی کاربر از حافظه بکاند خواهد بود. اگر محافظ الزام کند که کاربر باید در برابر حافظه بکاند اعتبارسنجی شود، در این صورت پیادهسازی بازیابی کاربر به ارائه دهنده احراز هویت سپرده میشود.
لاراول دو ارائه دهنده احراز هویت دارد که یکی Database و دیگری Eloquent است. ارائه دهنده احراز هویت Database اقدام به بازیابی مستقیم اطلاعات احراز هویت کاربر از حافظه بکاند میکند، در حالی که Eloquent لایه انتزاعی ارائه میکند که مراحل مورد نیاز را اجرا میکند.
در مثال مورد بررسی این مقاله، ما اقدام به پیادهسازی یک ارائهدهنده احراز هویت MongoDB میکنیم که اطلاعات احراز هویت کاربر را از بکاند MongoDB واکشی میکند.
بدین ترتیب مقدمه کوتاه ما در مورد محافظها و ارائه دهندهها در سیستم احراز هویت لاراول به پایان میرسد. در بخشهای بعدی روی توسعه محافظ و ارائه دهنده احراز هویت سفارشی خود متمرکز خواهیم شد.
نگاهی کوتاه به تنظیمات فایل
در ادامه به بررسی فهرست فایلهایی میپردازیم که در طی این مقاله پیادهسازی خواهیم کرد.
- config/auth.php: این فایل پیکربندی احراز هویت است که در آن یک مدخل برای محافظ سفارشی خود اضافه میکنیم.
- config/mongo.php: این فایلی است که پیکربندی MongoDB را نگهداری میکند.
- app/Services/Contracts/NosqlServiceInterface.php: این یک اینترفیس است که کلاس پایگاه داده Mongo سفارشی ما پیادهسازی میکند.
- app/Database/MongoDatabase.php: کلاس اصلی پایگاه داده است که با MongoDB تعامل دارد.
- app/Models/Auth/User.php: کلاس مدل «کاربر» (User) است که قرارداد احراز هویت را پیادهسازی میکند.
- app/Extensions/MongoUserProvider.php: یک پیادهسازی از ارائهدهنده احراز هویت است.
- app/Services/Auth/JsonGuard.php: پیادهسازی درایور محافظ احراز هویت است.
- app/Providers/AuthServiceProvider.php: یک فایل موجود است که از آن برای افزودن اتصالهای کانتینر سرویس خود استفاده میکنیم.
- app/Http/Controllers/MongoController.php: یک فایل کنترلر دمو است که برای تست محافظ سفارشی خود پیادهسازی میکنیم.
اگر فعلاً از فهرست فایلهای فوق چندان سر در نمیآورید، نباید نگران باشید، زیرا همه این موارد را در ادامه مقاله به تفصیل بررسی خواهیم کرد.
بررسی تفصیلی مراحل پیادهسازی
در این بخش به بررسی مراحل پیادهسازی فایلهای مورد نیاز خواهیم پرداخت.
نخستین کاری که باید انجام دهیم این است که در مورد محافظ سفارشی خود به لاراول اطلاع میدهیم. بدین منظور جزییات محافظ سفارشی را در فایل config/auth.php به صورت زیر وارد میکنیم:
1...
2...
3'guards' => [
4 'web' => [
5 'driver' => 'session',
6 'provider' => 'users',
7 ],
8
9 'api' => [
10 'driver' => 'token',
11 'provider' => 'users',
12 ],
13
14 'custom' => [
15 'driver' => 'json',
16 'provider' => 'mongo',
17 ],
18],
19...
20...
چنان که میبینید، محافظ سفارشی خود را زیر کلید Custom اضافه کردهایم. سپس باید یک مدخل ارائهدهنده مرتبط در بخش providers اضافه کنیم:
1...
2...
3'providers' => [
4 'users' => [
5 'driver' => 'eloquent',
6 'model' => App\User::class,
7 ],
8 'mongo' => [
9 'driver' => 'mongo'
10 ],
11
12 // 'users' => [
13 // 'driver' => 'database',
14 // 'table' => 'users',
15 // ],
16],
17...
18...
ما مدخل ارائهدهنده خود را زیر کلید mongo اضافه کردهایم. در نهایت محافظ احراز هویت پیشفرض خود را از «وب» به «سفارشی» عوض میکنیم:
1...
2...
3'defaults' => [
4 'guard' => 'custom',
5 'passwords' => 'users',
6],
7...
8...
البته این ارائهدهنده هنوز کار نمیکند، زیرا هنوز فایلهای ضروری را پیادهسازی نکردهایم. این همان است که در چند بخش بعدی برسی خواهیم کرد.
راهاندازی درایور MongoDB
در این بخش فایلهایی که برای مکالمه با وهله MongoDB مورد نیاز است را پیادهسازی میکنیم.
ابتدا یک فایل پیکربندی به نام config/mongo.php ایجاد میکنیم که تنظیمات پیشفرض اتصال MongoDB را نگهداری میکند.
1<?php
2return [
3 'defaults' => [
4 'host' => '{HOST_IP}',
5 'port' => '{HOST_PORT}',
6 'database' => '{DB_NAME}'
7 ]
8];
البته شما باید مقادیر را با تنظیمات خودتان عوض کنید. ما به جای ایجاد مستقیم یک کلاس که با MongoDB تعامل داشته باشد، در وهله نخست یک اینترفیس میسازیم. مزیت ایجاد یک اینترفیس این است که یک قرارداد ارائه میکند که هر توسعهدهندهای باید در زمان پیادهسازی از آن تبعیت کند. ضمناً پیادهسازی ما از MongoDB میتواند به سادگی درون پیادهسازی NoSQL دیگری قرار گیرد.
در ادامه یک فایل اینترفیس به نام app/Services/Contracts/NosqlServiceInterface.php با محتوای زیر ایجاد میکنیم:
1<?php
2// app/Services/Contracts/NosqlServiceInterface.php
3namespace App\Services\Contracts;
4
5Interface NosqlServiceInterface
6{
7 /**
8 * Create a Document
9 *
10 * @param string $collection Collection/Table Name
11 * @param array $document Document
12 * @return boolean
13 */
14 public function create($collection, Array $document);
15
16 /**
17 * Update a Document
18 *
19 * @param string $collection Collection/Table Name
20 * @param mix $id Primary Id
21 * @param array $document Document
22 * @return boolean
23 */
24 public function update($collection, $id, Array $document);
25
26 /**
27 * Delete a Document
28 *
29 * @param string $collection Collection/Table Name
30 * @param mix $id Primary Id
31 * @return boolean
32 */
33 public function delete($collection, $id);
34
35 /**
36 * Search Document(s)
37 *
38 * @param string $collection Collection/Table Name
39 * @param array $criteria Key-value criteria
40 * @return array
41 */
42 public function find($collection, Array $criteria);
43}
این یک اینترفیس کاملاً ساده است که متدهای ابتدایی CRUD را اعلان میکند و یک کلاس نیز اعلان میکند که در آن این اینترفیس را پیادهسازی میکند.
اینک میتوانیم یک کلاس واقعی در مسیر app/Database/MongoDatabase.php تعریف میکنیم:
1<?php
2// app/Database/MongoDatabase.php
3namespace App\Database;
4
5use App\Services\Contracts\NosqlServiceInterface;
6
7class MongoDatabase implements NosqlServiceInterface
8{
9 private $connection;
10 private $database;
11
12 public function __construct($host, $port, $database)
13 {
14 $this->connection = new MongoClient( "mongodb://{$host}:{$port}" );
15 $this->database = $this->connection->{$database};
16 }
17
18 /**
19 * @see \App\Services\Contracts\NosqlServiceInterface::find()
20 */
21 public function find($collection, Array $criteria)
22 {
23 return $this->database->{$collection}->findOne($criteria);
24 }
25
26 public function create($collection, Array $document) {}
27 public function update($collection, $id, Array $document) {}
28 public function delete($collection, $id) {}
29}
البته تصور میکنیم که شما قبلاً MongoDB و اکستنشن MongoDB PHP متناظر را نصب کردهاید.
متد construct__ یک کلاس را با پارامترهای ضروری وهلهسازی میکند. متد مهم دیگری که به آن علاقهمندیم متد find است که رکورد مبتنی بر معیار ارائه شده از سوی آرگومانهای متد را بازیابی میکند. این پیادهسازی درایور MongoDB بود که تلاش کردیم تا حد امکان آن را ساده حفظ کنیم.
راهاندازی مدل User
ما با پیروی از استانداردهای سیستم احراز هویت مدل User را پیادهسازی میکنیم که باید قرارداد را پیادهسازی کند. در ادامه یک فایل به نام app/Models/Auth/User.php با محتوای زیر ایجاد میکنیم:
1<?php
2// app/Models/Auth/User.php
3namespace App\Models\Auth;
4
5use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
6use App\Services\Contracts\NosqlServiceInterface;
7
8class User implements AuthenticatableContract
9{
10 private $conn;
11
12 private $username;
13 private $password;
14 protected $rememberTokenName = 'remember_token';
15
16 public function __construct(NosqlServiceInterface $conn)
17 {
18 $this->conn = $conn;
19 }
20
21 /**
22 * Fetch user by Credentials
23 *
24 * @param array $credentials
25 * @return Illuminate\Contracts\Auth\Authenticatable
26 */
27 public function fetchUserByCredentials(Array $credentials)
28 {
29 $arr_user = $this->conn->find('users', ['username' => $credentials['username']]);
30
31 if (! is_null($arr_user)) {
32 $this->username = $arr_user['username'];
33 $this->password = $arr_user['password'];
34 }
35
36 return $this;
37 }
38
39 /**
40 * {@inheritDoc}
41 * @see \Illuminate\Contracts\Auth\Authenticatable::getAuthIdentifierName()
42 */
43 public function getAuthIdentifierName()
44 {
45 return "username";
46 }
47
48 /**
49 * {@inheritDoc}
50 * @see \Illuminate\Contracts\Auth\Authenticatable::getAuthIdentifier()
51 */
52 public function getAuthIdentifier()
53 {
54 return $this->{$this->getAuthIdentifierName()};
55 }
56
57 /**
58 * {@inheritDoc}
59 * @see \Illuminate\Contracts\Auth\Authenticatable::getAuthPassword()
60 */
61 public function getAuthPassword()
62 {
63 return $this->password;
64 }
65
66 /**
67 * {@inheritDoc}
68 * @see \Illuminate\Contracts\Auth\Authenticatable::getRememberToken()
69 */
70 public function getRememberToken()
71 {
72 if (! empty($this->getRememberTokenName())) {
73 return $this->{$this->getRememberTokenName()};
74 }
75 }
76
77 /**
78 * {@inheritDoc}
79 * @see \Illuminate\Contracts\Auth\Authenticatable::setRememberToken()
80 */
81 public function setRememberToken($value)
82 {
83 if (! empty($this->getRememberTokenName())) {
84 $this->{$this->getRememberTokenName()} = $value;
85 }
86 }
87
88 /**
89 * {@inheritDoc}
90 * @see \Illuminate\Contracts\Auth\Authenticatable::getRememberTokenName()
91 */
92 public function getRememberTokenName()
93 {
94 return $this->rememberTokenName;
95 }
96}
احتمالاً تاکنون متوجه شدهاید که App\Models\Auth\User قرارداد Illuminate\Contracts\Auth\Authenticatable را پیادهسازی میکند.
اغلب متدهایی که در کلاس ما پیادهسازی شدهاند کاملاً گویا هستند. مثلاً متد fetchUserByCredentials کاربر را از بکاند موجود بازیابی میکند. در این مثال، بکاند، کلاس MongoDatabase است که برای بازیابی اطلاعات مورد نیز فراخوانی خواهد شد. بدین ترتیب کار پیادهسازی مدل User به پایان میرسد.
راهاندازی ارائه دهنده احراز هویت
چنان که پیشتر اشاره کردیم، سیستم احراز هویت لاراول شامل دو عنصر محافظ و ارائه دهنده است. در این بخش یک ارائه دهنده احراز هویت میسازیم که به منظور بازیابی کاربر از بکاند مورد استفاده قرار میگیرد. در ادامه فایلی به نام app/Extensions/MongoUserProvider.php با محتوای زیر ایجاد میکنیم:
1<?php
2// app/Extensions/MongoUserProvider.php
3namespace App\Extensions;
4
5use Illuminate\Support\Str;
6use Illuminate\Contracts\Auth\UserProvider;
7use Illuminate\Contracts\Auth\Authenticatable;
8
9class MongoUserProvider implements UserProvider
10{
11 /**
12 * The Mongo User Model
13 */
14 private $model;
15
16 /**
17 * Create a new mongo user provider.
18 *
19 * @return \Illuminate\Contracts\Auth\Authenticatable|null
20 * @return void
21 */
22 public function __construct(\App\Models\Auth\User $userModel)
23 {
24 $this->model = $userModel;
25 }
26
27 /**
28 * Retrieve a user by the given credentials.
29 *
30 * @param array $credentials
31 * @return \Illuminate\Contracts\Auth\Authenticatable|null
32 */
33 public function retrieveByCredentials(array $credentials)
34 {
35 if (empty($credentials)) {
36 return;
37 }
38
39 $user = $this->model->fetchUserByCredentials(['username' => $credentials['username']]);
40
41 return $user;
42 }
43
44 /**
45 * Validate a user against the given credentials.
46 *
47 * @param \Illuminate\Contracts\Auth\Authenticatable $user
48 * @param array $credentials Request credentials
49 * @return bool
50 */
51 public function validateCredentials(Authenticatable $user, Array $credentials)
52 {
53 return ($credentials['username'] == $user->getAuthIdentifier() &&
54 md5($credentials['password']) == $user->getAuthPassword());
55 }
56
57 public function retrieveById($identifier) {}
58
59 public function retrieveByToken($identifier, $token) {}
60
61 public function updateRememberToken(Authenticatable $user, $token) {}
62}
در این مورد نیز باید مطمئن شویم که ارائهدهنده سفارشی حتماً قرارداد Illuminate\Contracts\Auth\UserProvider را پیادهسازی میکند در ادامه دو متد مهم به نامهای retrieveByCredentials و validateCredentials تعریف میکنیم.
متد retrieveByCredentials برای بازیابی احراز هویت با استفاده از کلاس مدل User استفاده میشود که در بخش قبلی ساختهایم. از سوی دیگر متد validateCredentials برای اعتبارسنجی کاربر در برابر مجموعه مفروضی از اطلاعات احراز هویت مورد استفاده قرار میگیرد.
بدین ترتیب کار پیادهسازی ارائه دهنده سفارشی ما نیز به پایان میرسد. در بخش بعدی یک محافظ ایجاد میکنیم که با ارائه دهنده احراز هویت MongoUserProvider تعامل دارد.
راهاندازی محافظ احراز هویت
چنان که قبلاً بررسی کردیم، محافظ در سیسم احراز هویت لاراول، شیوه احراز هویت کاربر را تدارک میبیند. در مثال عملی مورد بررسی در این مقاله ما اقدام به بررسی پارامتر درخواست jsondata میکنیم که باید شامل رشته اطلاعات احراز هویت باشد که به صورت JSON انکود شده است.
در این بخش یک محافظ ایجاد میکنیم که با ارائهدهنده احراز هویت که در بخش قبل ساختیم تعامل دارد. در ادامه فایلی به نام app/Services/Auth/JsonGuard.php و با محتوای زیر ایجاد میکنیم:
1<?php
2// app/Services/Auth/JsonGuard.php
3namespace App\Services\Auth;
4
5use Illuminate\Http\Request;
6use Illuminate\Contracts\Auth\Guard;
7use Illuminate\Contracts\Auth\UserProvider;
8use GuzzleHttp\json_decode;
9use phpDocumentor\Reflection\Types\Array_;
10use Illuminate\Contracts\Auth\Authenticatable;
11
12class JsonGuard implements Guard
13{
14 protected $request;
15 protected $provider;
16 protected $user;
17
18 /**
19 * Create a new authentication guard.
20 *
21 * @param \Illuminate\Contracts\Auth\UserProvider $provider
22 * @param \Illuminate\Http\Request $request
23 * @return void
24 */
25 public function __construct(UserProvider $provider, Request $request)
26 {
27 $this->request = $request;
28 $this->provider = $provider;
29 $this->user = NULL;
30 }
31
32 /**
33 * Determine if the current user is authenticated.
34 *
35 * @return bool
36 */
37 public function check()
38 {
39 return ! is_null($this->user());
40 }
41
42 /**
43 * Determine if the current user is a guest.
44 *
45 * @return bool
46 */
47 public function guest()
48 {
49 return ! $this->check();
50 }
51
52 /**
53 * Get the currently authenticated user.
54 *
55 * @return \Illuminate\Contracts\Auth\Authenticatable|null
56 */
57 public function user()
58 {
59 if (! is_null($this->user)) {
60 return $this->user;
61 }
62 }
63
64 /**
65 * Get the JSON params from the current request
66 *
67 * @return string
68 */
69 public function getJsonParams()
70 {
71 $jsondata = $this->request->query('jsondata');
72
73 return (!empty($jsondata) ? json_decode($jsondata, TRUE) : NULL);
74 }
75
76 /**
77 * Get the ID for the currently authenticated user.
78 *
79 * @return string|null
80 */
81 public function id()
82 {
83 if ($user = $this->user()) {
84 return $this->user()->getAuthIdentifier();
85 }
86 }
87
88 /**
89 * Validate a user's credentials.
90 *
91 * @return bool
92 */
93 public function validate(Array $credentials=[])
94 {
95 if (empty($credentials['username']) || empty($credentials['password'])) {
96 if (!$credentials=$this->getJsonParams()) {
97 return false;
98 }
99 }
100
101 $user = $this->provider->retrieveByCredentials($credentials);
102
103 if (! is_null($user) && $this->provider->validateCredentials($user, $credentials)) {
104 $this->setUser($user);
105
106 return true;
107 } else {
108 return false;
109 }
110 }
111
112 /**
113 * Set the current user.
114 *
115 * @param Array $user User info
116 * @return void
117 */
118 public function setUser(Authenticatable $user)
119 {
120 $this->user = $user;
121 return $this;
122 }
123}
قبل از هر چیز کلاس ما باید اینترفیس Illuminate\Contracts\Auth\Guard را پیادهسازی کند. بدین ترتیب باید همه متدهای اعلان شده در آن اینترفیس را پیادهسازی کنیم.
نکته مهمی که باید اشاره کرد این است که تابع construct__ نیازمند یک پیادهسازی از Illuminate\Contracts\Auth\UserProvider است. در این مورد یک وهله از App\Extensions\MongoUserProvider ارسال میکنیم که در بخش بعدی آن را بررسی خواهیم کرد.
سپس یک تابع به نام getJsonParams داریم که اطلاعات احراز هویت کاربر را از پارامتر درخواست با نام jsondata بازیابی میکند. چنان که انتظار میرود ما اقدام به بازیابی یک رشته انکود شده JSON از اطلاعات احراز هویت کاربر میکنیم. به این منظور از تابع json_decode برای دیکود کردن دادههای JSON استفاده کردهایم.
در تابع اعتبارسنجی، نخستین کاری که انجام میدهیم، بررسی وجود آرگومان credentials$ است. اگر این آرگومان موجود نباشد، متد getJsonParams را برای بازیابی اطلاعات احراز هویت کاربر از پارامترهای درخواست مورد استفاده قرار میدهیم.
در ادامه متد retrieveByCredentials را از ارائهدهنده MongoUserProvider فراخوانی میکنیم که کاربر را از پایگاه داده MongoDB بکاند بازیابی میکند. در نهایت متد validateCredentials ارائهدهنده MongoUserProvider اقدام به اعتبارسنجی کاربر میکند.
بدین ترتیب کار پیادهسازی محافظ سفارشی به پایان میرسد. در بخش بعدی شیوه کنار هم قرار دادن همه این قطعات مختلف برای ساخت یک سیستم احراز هویت موفق را توضیح میدهیم.
جمعبندی اجزای مختلف
تا به اینجا همه اجزای محافظ احراز هویت سفارشی که سیستم احراز هویت جدید ما را تشکیل میدهند طراحی کرده و توسعه دادهایم. با این حال، این سیستم هنوز آماده به کار نیست، زیرا باید اول از اتصالهای کانتینر سرویس لاراول استفاده کنیم تا آن را ثبت نماییم. چنان که احتمالاً میدانید، ارائهدهنده سرویس لاراول بهترین مکان برای پیادهسازی اتصال مورد نیاز است.
در ادامه فایل app/Providers/AuthServiceProvider.php را باز میکنیم که امکان افزودن اتصالهای کانتینر سرویس احراز هویت را به ما میدهد. اگر این فایل شامل هیچ تغییر سفارشی نباشد، میتوانید آن را با محتوای زیر تعویض کنید:
1<?php
2// app/Providers/AuthServiceProvider.php
3namespace App\Providers;
4
5use Illuminate\Support\Facades\Auth;
6use Illuminate\Support\Facades\Gate;
7use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
8use App\Services\Auth\JsonGuard;
9use App\Extensions\MongoUserProvider;
10use App\Database\MongoDatabase;
11use App\Models\Auth\User;
12use Illuminate\Http\Request;
13use Illuminate\Support\Facades\Config;
14
15class AuthServiceProvider extends ServiceProvider
16{
17 /**
18 * The policy mappings for the application.
19 *
20 * @var array
21 */
22 protected $policies = [
23 'App\Model' => 'App\Policies\ModelPolicy',
24 ];
25
26 /**
27 * Register any authentication / authorization services.
28 *
29 * @return void
30 */
31 public function boot()
32 {
33 $this->registerPolicies();
34
35 $this->app->bind('App\Database\MongoDatabase', function ($app) {
36 return new MongoDatabase(config('mongo.defaults.host'), config('mongo.defaults.port'), config('mongo.defaults.database'));
37 });
38
39 $this->app->bind('App\Models\Auth\User', function ($app) {
40 return new User($app->make('App\Database\MongoDatabase'));
41 });
42
43 // add custom guard provider
44 Auth::provider('mongo', function ($app, array $config) {
45 return new MongoUserProvider($app->make('App\Models\Auth\User'));
46 });
47
48 // add custom guard
49 Auth::extend('json', function ($app, $name, array $config) {
50 return new JsonGuard(Auth::createUserProvider($config['provider']), $app->make('request'));
51 });
52 }
53
54 public function register()
55 {
56 $this->app->bind(
57 'App\Services\Contracts\NosqlServiceInterface',
58 'App\Database\MongoDatabase'
59 );
60 }
61}
در ادامه متد boot را مینویسیم که شامل اغلب اتصالهای ارائهدهنده است. در آغاز کار اتصالهایی برای عناصر App\Database\MongoDatabase و App\Models\Auth\User ایجاد میکنیم.
1$this->app->bind('App\Database\MongoDatabase', function ($app) {
2 return new MongoDatabase(config('mongo.defaults.host'), config('mongo.defaults.port'), config('mongo.defaults.database'));
3});
4
5$this->app->bind('App\Models\Auth\User', function ($app) {
6 return new User($app->make('App\Database\MongoDatabase'));
7});
اینک زمان آن رسیده است که محافظ و ارائهدهنده سفارشی را به سیستم احراز هویت لاراول وصل کنیم. ما از متد Auth Facade ارائهدهنده برای افزودن ارائهدهنده احراز هویت سفارشی خود زیر کلید mongo استفاده میکنیم. به خاطر داشته باشید که این کلید تنظیمات انجام یافته قبلی در فایل auth.php را بازتاب میدهد.
1Auth::provider('mongo', function ($app, array $config) {
2 return new MongoUserProvider($app->make('App\Models\Auth\User'));
3});
به طور مشابه، پیادهسازی محافظ سفارشی خود را با استفاده از متد extend مربوط به Auth facade تزریق میکنیم.
1Auth::extend('json', function ($app, $name, array $config) {
2 return new JsonGuard(Auth::createUserProvider($config['provider']), $app->make('request'));
3});
در ادامه یک متد register وجود دارد که از آن برای اتصال اینترفیس App\Services\Contracts\NosqlServiceInterface interface به پیادهسازی App\Database\MongoDatabase استفاده میکنیم.
1
2$this->app->bind(
3 'App\Services\Contracts\NosqlServiceInterface',
4 'App\Database\MongoDatabase'
5);
بدین ترتیب هر زمان که لازم باشد وابستگی App\Services\Contracts\NosqlServiceInterface برقرار شود، لاراول با پیادهسازی آداپتر App\Database\MongoDatabase پاسخ میدهد.
مزیت استفاده از این رویکرد آن است که فرد میتواند به سادگی پیادهسازی مفروض را با پیادهسازی سفارشی تعویض کند. برای نمونه تصور کنید فردی میخواهد در آینده پیادهسازی App\Database\MongoDatabase را با آداپتر CouchDB عوض کند. در این حالت، کافی است اتصال متناظر را در متد register اضافه کند.
بدین ترتیب ارائهدهنده سرویس آماده به کار شده است. در این زمان ما همه موارد لازم برای تست پیادهسازی محافظ سفارشی خود را در اختیار داریم، بنابراین در بخش بعدی به تست آن میپردازیم.
آیا سیستم ما کار میکند؟ بدین ترتیب ما همه مراحل دشوار محافظ احراز هویت سفارشی اول خود را سپری کردهایم و اینک زمان آن رسیده که از آن استفاده کرده و آن را امتحان کنیم.
در ادامه یک فایل کنترلر ابتدایی به نام app/Http/Controllers/MongoController.php مانند زیر پیادهسازی کردیم:
1<?php
2// app/Http/Controllers/MongoController.php
3namespace App\Http\Controllers;
4
5use App\Http\Controllers\Controller;
6use Illuminate\Contracts\Auth\Guard;
7
8class MongoController extends Controller
9{
10 public function login(Guard $auth_guard)
11 {
12 if ($auth_guard->validate()) {
13 // get the current authenticated user
14 $user = $auth_guard->user();
15
16 echo 'Success!';
17 } else {
18 echo 'Not authorized to access this page!';
19 }
20 }
21}
در ادامه نگاهی دقیق به وابستگی متد login میاندازیم که نیازمند پیادهسازی محافظ Illuminate\Contracts\Auth\Guard است. از آنجا که ما محافظ custom را به عنوان محافظ پیشفرض در فایل auth.php تنظیم کردیم، در واقع این App\Services\Auth\JsonGuard است که تزریق میشود.
سپس متد validate را از کلاس App\Services\Auth\JsonGuard فراخوانی میکنیم که به نوبه خود یک سری از فراخوانیهای متد را دارد:
- متد retrieveByCredentials را از کلاس App\Extensions\MongoUserProvider فراخوانی میکند.
- متد retrieveByCredentials اقدام به فراخوانی متد fetchUserByCredentials از کلاس User در مسیر App\Models\Auth\User میکند.
- متد fetchUserByCredentials متد find را از کلاس App\Database\MongoDatabase برای بازیابی اطلاعات احراز هویت فراخوانی میکند.
- در نهایت متد find از App\Database\MongoDatabase پاسخ را بازگشت میدهد.
اگر همه چیز مطابق انتظار اجرا شود، با فراخوانی متد user در محافظ خودمان یک کاربر احراز هویت شده به دست میآوریم.
برای دسترسی به کنترلر باید یک مسیر مرتبط در فایل routes/web.php اضافه کنید.
1Route::get('/custom/mongo/login', 'MongoController@login');
اگر تلاش کنید بدون هر گونه پارامتری به مسیر http://your-laravel-site/custom/mongo/login دسترسی پیدا کنید، با پیام not authorized مواجه میشوید.
از سوی دیگر، اگر آدرسی مانند آنچه در زیر آمده است را وارد کنید در پاسخ در صورتی که کاربر در پایگاه داده موجود باشد، پیام موفقیت را دریافت خواهید کرد.
http://your-laravel-site/custom/mongo/login?jsondata={"username":"admin","password":"admin"}
توجه داشته باشید که این موارد صرفاً به عنوان نمونه هستند و قصد ما نمایش طرز کار محافظ سفارشی است. شما در عمل باید از طرز عمل گردش احراز هویت و مسئولیت خود برای ایجاد یک راهحل پایدار و امن برای اپلیکیشن آگاه باشید.
سخن پایانی
فریمورک لاراول یک سیستم احراز هویت پایدار در هسته مرکزی خود دارد که در صورت نیاز میتواند برای ساخت یک سیستم احراز هویت سفارشی بسط یابد. در این مقاله به بررسی روش پیادهسازی یک محافظ سفارشی و اتصال آن به گردش کار احراز هویت لاراول پرداختیم. در طی این مسیر یک سیستم توسعه دادیم که کاربر بر مبنای یک رشته JSON در درخواست که با پایگاه داده MongoDB تطبیق مییافت، احراز هویت میشود. برای نیل به این مقصود در نهایت یک محافظ سفارشی و یک ارائهدهنده سفارشی را پیادهسازی کردیم.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی PHP
- آموزش REST API در Laravel (لاراول) با بسته Passport
- مجموعه آموزشهای برنامهنویسی
- جستجوی تمام متن در لاراول با Scout — به زبان ساده
- مدیریت پکیج در لاراول — به زبان ساده
==