در این مقاله، قصد داریم سیستم احراز هویت فریمورک لاراول را مورد بررسی قرار دهیم. هدف اصلی این مقاله ایجاد یک راهکار حفاظتی احراز هویت سفارشی در لاراول است که از طریق بسط سیستم احراز هویت مرکزی آن صورت میگیرد.
لاراول در هسته خود یک سیستم احراز هویت کاملاً پایدار دارد که پیادهسازی کارکرد احراز هویت را به سادگی امکانپذیر ساخته است. در واقع کافی است چند دستور آرتیزان اجرا کنید تا چارچوب یک سیستم احراز هویت آماده شود.
به علاوه خود سیستم طوری طراحی شده است که میتوان آن را بسط داد و به آداپترهای احراز هویت سفارشی نیز وصل کرد. این همان کاری است که قرار است در این مقاله انجام دهیم. پیش از آن که پا را فراتر گذارده و وارد جزییات پیادهسازی محافظ احراز هویت سفارشی شویم، کار خود را با بررسی عناصر ابتدایی سیستم احراز هویت آغاز میکنیم که محافظها و ارائه دهندهها هستند.
سیستم احراز هویت سفارشی لاراول از دو عنصر اصلی به نام «محافظ» (Guard) و «ارائه دهنده» (Provider) تشکیل یافته است.
محافظ را میتوان به چشم راهحل ارائه دهنده منطق برای شناسایی کاربران احراز هویت شده دید. لاراول در هسته مرکزی خود محافظهای مختلفی مانند «نشست» (Session) و «توکن» (Token) ارائه میکند. محافظ نشست، حالت کاربر را در هر درخواست به وسیله کوکی نگهداری میکند و در سوی دیگر محافظ توکن کاربر را از طریق یک توکن معتبر در هر درخواست احراز هویت میکند.
بنابراین چنان که میبینید محافظ، منطق احراز هویت را تعریف میکند و لزومی نیست که همیشه اقدام به بازیابی اطلاعات احراز هویت معتبر از بکاند بکند. شما میتوانید یک محافظ را که به سادگی حضور یک شیء خاص را در هدر درخواست بررسی میکند و کاربر را بر مبنای آن احراز هویت میکند پیادهسازی کنید.
در ادامه این مقاله ما یک محافظ پیادهسازی میکنیم که پارامترهای خاص JSON را در هدرهای درخواست بررسی میکند و کاربر معتبر را از بکاند بازیابی مینماید.
اگر محافظ منطق احراز هویت را تعریف کند، ارائه دهنده احراز هویت مسئول بازیابی کاربر از حافظه بکاند خواهد بود. اگر محافظ الزام کند که کاربر باید در برابر حافظه بکاند اعتبارسنجی شود، در این صورت پیادهسازی بازیابی کاربر به ارائه دهنده احراز هویت سپرده میشود.
لاراول دو ارائه دهنده احراز هویت دارد که یکی Database و دیگری Eloquent است. ارائه دهنده احراز هویت Database اقدام به بازیابی مستقیم اطلاعات احراز هویت کاربر از حافظه بکاند میکند، در حالی که Eloquent لایه انتزاعی ارائه میکند که مراحل مورد نیاز را اجرا میکند.
در مثال مورد بررسی این مقاله، ما اقدام به پیادهسازی یک ارائهدهنده احراز هویت MongoDB میکنیم که اطلاعات احراز هویت کاربر را از بکاند MongoDB واکشی میکند.
بدین ترتیب مقدمه کوتاه ما در مورد محافظها و ارائه دهندهها در سیستم احراز هویت لاراول به پایان میرسد. در بخشهای بعدی روی توسعه محافظ و ارائه دهنده احراز هویت سفارشی خود متمرکز خواهیم شد.
در ادامه به بررسی فهرست فایلهایی میپردازیم که در طی این مقاله پیادهسازی خواهیم کرد.
اگر فعلاً از فهرست فایلهای فوق چندان سر در نمیآورید، نباید نگران باشید، زیرا همه این موارد را در ادامه مقاله به تفصیل بررسی خواهیم کرد.
در این بخش به بررسی مراحل پیادهسازی فایلهای مورد نیاز خواهیم پرداخت.
نخستین کاری که باید انجام دهیم این است که در مورد محافظ سفارشی خود به لاراول اطلاع میدهیم. بدین منظور جزییات محافظ سفارشی را در فایل config/auth.php به صورت زیر وارد میکنیم:
چنان که میبینید، محافظ سفارشی خود را زیر کلید Custom اضافه کردهایم. سپس باید یک مدخل ارائهدهنده مرتبط در بخش providers اضافه کنیم:
ما مدخل ارائهدهنده خود را زیر کلید mongo اضافه کردهایم. در نهایت محافظ احراز هویت پیشفرض خود را از «وب» به «سفارشی» عوض میکنیم:
البته این ارائهدهنده هنوز کار نمیکند، زیرا هنوز فایلهای ضروری را پیادهسازی نکردهایم. این همان است که در چند بخش بعدی برسی خواهیم کرد.
در این بخش فایلهایی که برای مکالمه با وهله MongoDB مورد نیاز است را پیادهسازی میکنیم.
ابتدا یک فایل پیکربندی به نام config/mongo.php ایجاد میکنیم که تنظیمات پیشفرض اتصال MongoDB را نگهداری میکند.
البته شما باید مقادیر را با تنظیمات خودتان عوض کنید. ما به جای ایجاد مستقیم یک کلاس که با MongoDB تعامل داشته باشد، در وهله نخست یک اینترفیس میسازیم. مزیت ایجاد یک اینترفیس این است که یک قرارداد ارائه میکند که هر توسعهدهندهای باید در زمان پیادهسازی از آن تبعیت کند. ضمناً پیادهسازی ما از MongoDB میتواند به سادگی درون پیادهسازی NoSQL دیگری قرار گیرد.
در ادامه یک فایل اینترفیس به نام app/Services/Contracts/NosqlServiceInterface.php با محتوای زیر ایجاد میکنیم:
این یک اینترفیس کاملاً ساده است که متدهای ابتدایی CRUD را اعلان میکند و یک کلاس نیز اعلان میکند که در آن این اینترفیس را پیادهسازی میکند.
اینک میتوانیم یک کلاس واقعی در مسیر app/Database/MongoDatabase.php تعریف میکنیم:
البته تصور میکنیم که شما قبلاً MongoDB و اکستنشن MongoDB PHP متناظر را نصب کردهاید.
متد construct__ یک کلاس را با پارامترهای ضروری وهلهسازی میکند. متد مهم دیگری که به آن علاقهمندیم متد find است که رکورد مبتنی بر معیار ارائه شده از سوی آرگومانهای متد را بازیابی میکند. این پیادهسازی درایور MongoDB بود که تلاش کردیم تا حد امکان آن را ساده حفظ کنیم.
ما با پیروی از استانداردهای سیستم احراز هویت مدل User را پیادهسازی میکنیم که باید قرارداد را پیادهسازی کند. در ادامه یک فایل به نام app/Models/Auth/User.php با محتوای زیر ایجاد میکنیم:
احتمالاً تاکنون متوجه شدهاید که App\Models\Auth\User قرارداد Illuminate\Contracts\Auth\Authenticatable را پیادهسازی میکند.
اغلب متدهایی که در کلاس ما پیادهسازی شدهاند کاملاً گویا هستند. مثلاً متد fetchUserByCredentials کاربر را از بکاند موجود بازیابی میکند. در این مثال، بکاند، کلاس MongoDatabase است که برای بازیابی اطلاعات مورد نیز فراخوانی خواهد شد. بدین ترتیب کار پیادهسازی مدل User به پایان میرسد.
چنان که پیشتر اشاره کردیم، سیستم احراز هویت لاراول شامل دو عنصر محافظ و ارائه دهنده است. در این بخش یک ارائه دهنده احراز هویت میسازیم که به منظور بازیابی کاربر از بکاند مورد استفاده قرار میگیرد. در ادامه فایلی به نام app/Extensions/MongoUserProvider.php با محتوای زیر ایجاد میکنیم:
در این مورد نیز باید مطمئن شویم که ارائهدهنده سفارشی حتماً قرارداد Illuminate\Contracts\Auth\UserProvider را پیادهسازی میکند در ادامه دو متد مهم به نامهای retrieveByCredentials و validateCredentials تعریف میکنیم.
متد retrieveByCredentials برای بازیابی احراز هویت با استفاده از کلاس مدل User استفاده میشود که در بخش قبلی ساختهایم. از سوی دیگر متد validateCredentials برای اعتبارسنجی کاربر در برابر مجموعه مفروضی از اطلاعات احراز هویت مورد استفاده قرار میگیرد.
بدین ترتیب کار پیادهسازی ارائه دهنده سفارشی ما نیز به پایان میرسد. در بخش بعدی یک محافظ ایجاد میکنیم که با ارائه دهنده احراز هویت MongoUserProvider تعامل دارد.
چنان که قبلاً بررسی کردیم، محافظ در سیسم احراز هویت لاراول، شیوه احراز هویت کاربر را تدارک میبیند. در مثال عملی مورد بررسی در این مقاله ما اقدام به بررسی پارامتر درخواست jsondata میکنیم که باید شامل رشته اطلاعات احراز هویت باشد که به صورت JSON انکود شده است.
در این بخش یک محافظ ایجاد میکنیم که با ارائهدهنده احراز هویت که در بخش قبل ساختیم تعامل دارد. در ادامه فایلی به نام app/Services/Auth/JsonGuard.php و با محتوای زیر ایجاد میکنیم:
قبل از هر چیز کلاس ما باید اینترفیس 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 را باز میکنیم که امکان افزودن اتصالهای کانتینر سرویس احراز هویت را به ما میدهد. اگر این فایل شامل هیچ تغییر سفارشی نباشد، میتوانید آن را با محتوای زیر تعویض کنید:
در ادامه متد boot را مینویسیم که شامل اغلب اتصالهای ارائهدهنده است. در آغاز کار اتصالهایی برای عناصر App\Database\MongoDatabase و App\Models\Auth\User ایجاد میکنیم.
اینک زمان آن رسیده است که محافظ و ارائهدهنده سفارشی را به سیستم احراز هویت لاراول وصل کنیم. ما از متد Auth Facade ارائهدهنده برای افزودن ارائهدهنده احراز هویت سفارشی خود زیر کلید mongo استفاده میکنیم. به خاطر داشته باشید که این کلید تنظیمات انجام یافته قبلی در فایل auth.php را بازتاب میدهد.
به طور مشابه، پیادهسازی محافظ سفارشی خود را با استفاده از متد extend مربوط به Auth facade تزریق میکنیم.
در ادامه یک متد register وجود دارد که از آن برای اتصال اینترفیس App\Services\Contracts\NosqlServiceInterface interface به پیادهسازی App\Database\MongoDatabase استفاده میکنیم.
بدین ترتیب هر زمان که لازم باشد وابستگی App\Services\Contracts\NosqlServiceInterface برقرار شود، لاراول با پیادهسازی آداپتر App\Database\MongoDatabase پاسخ میدهد.
مزیت استفاده از این رویکرد آن است که فرد میتواند به سادگی پیادهسازی مفروض را با پیادهسازی سفارشی تعویض کند. برای نمونه تصور کنید فردی میخواهد در آینده پیادهسازی App\Database\MongoDatabase را با آداپتر CouchDB عوض کند. در این حالت، کافی است اتصال متناظر را در متد register اضافه کند.
بدین ترتیب ارائهدهنده سرویس آماده به کار شده است. در این زمان ما همه موارد لازم برای تست پیادهسازی محافظ سفارشی خود را در اختیار داریم، بنابراین در بخش بعدی به تست آن میپردازیم.
آیا سیستم ما کار میکند؟ بدین ترتیب ما همه مراحل دشوار محافظ احراز هویت سفارشی اول خود را سپری کردهایم و اینک زمان آن رسیده که از آن استفاده کرده و آن را امتحان کنیم.
در ادامه یک فایل کنترلر ابتدایی به نام app/Http/Controllers/MongoController.php مانند زیر پیادهسازی کردیم:
در ادامه نگاهی دقیق به وابستگی متد login میاندازیم که نیازمند پیادهسازی محافظ Illuminate\Contracts\Auth\Guard است. از آنجا که ما محافظ custom را به عنوان محافظ پیشفرض در فایل auth.php تنظیم کردیم، در واقع این App\Services\Auth\JsonGuard است که تزریق میشود.
سپس متد validate را از کلاس App\Services\Auth\JsonGuard فراخوانی میکنیم که به نوبه خود یک سری از فراخوانیهای متد را دارد:
اگر همه چیز مطابق انتظار اجرا شود، با فراخوانی متد user در محافظ خودمان یک کاربر احراز هویت شده به دست میآوریم.
برای دسترسی به کنترلر باید یک مسیر مرتبط در فایل routes/web.php اضافه کنید.
اگر تلاش کنید بدون هر گونه پارامتری به مسیر http://your-laravel-site/custom/mongo/login دسترسی پیدا کنید، با پیام not authorized مواجه میشوید.
از سوی دیگر، اگر آدرسی مانند آنچه در زیر آمده است را وارد کنید در پاسخ در صورتی که کاربر در پایگاه داده موجود باشد، پیام موفقیت را دریافت خواهید کرد.
http://your-laravel-site/custom/mongo/login?jsondata={“username”:”admin”,”password”:”admin”}
توجه داشته باشید که این موارد صرفاً به عنوان نمونه هستند و قصد ما نمایش طرز کار محافظ سفارشی است. شما در عمل باید از طرز عمل گردش احراز هویت و مسئولیت خود برای ایجاد یک راهحل پایدار و امن برای اپلیکیشن آگاه باشید.
فریمورک لاراول یک سیستم احراز هویت پایدار در هسته مرکزی خود دارد که در صورت نیاز میتواند برای ساخت یک سیستم احراز هویت سفارشی بسط یابد. در این مقاله به بررسی روش پیادهسازی یک محافظ سفارشی و اتصال آن به گردش کار احراز هویت لاراول پرداختیم. در طی این مسیر یک سیستم توسعه دادیم که کاربر بر مبنای یک رشته JSON در درخواست که با پایگاه داده MongoDB تطبیق مییافت، احراز هویت میشود. برای نیل به این مقصود در نهایت یک محافظ سفارشی و یک ارائهدهنده سفارشی را پیادهسازی کردیم.
ا
منبع: فرادرس
در این مقاله، قصد داریم سیستم احراز هویت فریمورک لاراول را مورد بررسی قرار دهیم. هدف اصلی این مقاله ایجاد یک راهکار حفاظتی احراز هویت سفارشی در لاراول است که از طریق بسط سیستم احراز هویت مرکزی آن صورت میگیرد.
لاراول در هسته خود یک سیستم احراز هویت کاملاً پایدار دارد که پیادهسازی کارکرد احراز هویت را به سادگی امکانپذیر ساخته است. در واقع کافی است چند دستور آرتیزان اجرا کنید تا چارچوب یک سیستم احراز هویت آماده شود.
به علاوه خود سیستم طوری طراحی شده است که میتوان آن را بسط داد و به آداپترهای احراز هویت سفارشی نیز وصل کرد. این همان کاری است که قرار است در این مقاله انجام دهیم. پیش از آن که پا را فراتر گذارده و وارد جزییات پیادهسازی محافظ احراز هویت سفارشی شویم، کار خود را با بررسی عناصر ابتدایی سیستم احراز هویت آغاز میکنیم که محافظها و ارائه دهندهها هستند.
سیستم احراز هویت سفارشی لاراول از دو عنصر اصلی به نام «محافظ» (Guard) و «ارائه دهنده» (Provider) تشکیل یافته است.
محافظ را میتوان به چشم راهحل ارائه دهنده منطق برای شناسایی کاربران احراز هویت شده دید. لاراول در هسته مرکزی خود محافظهای مختلفی مانند «نشست» (Session) و «توکن» (Token) ارائه میکند. محافظ نشست، حالت کاربر را در هر درخواست به وسیله کوکی نگهداری میکند و در سوی دیگر محافظ توکن کاربر را از طریق یک توکن معتبر در هر درخواست احراز هویت میکند.
بنابراین چنان که میبینید محافظ، منطق احراز هویت را تعریف میکند و لزومی نیست که همیشه اقدام به بازیابی اطلاعات احراز هویت معتبر از بکاند بکند. شما میتوانید یک محافظ را که به سادگی حضور یک شیء خاص را در هدر درخواست بررسی میکند و کاربر را بر مبنای آن احراز هویت میکند پیادهسازی کنید.
در ادامه این مقاله ما یک محافظ پیادهسازی میکنیم که پارامترهای خاص JSON را در هدرهای درخواست بررسی میکند و کاربر معتبر را از بکاند بازیابی مینماید.
اگر محافظ منطق احراز هویت را تعریف کند، ارائه دهنده احراز هویت مسئول بازیابی کاربر از حافظه بکاند خواهد بود. اگر محافظ الزام کند که کاربر باید در برابر حافظه بکاند اعتبارسنجی شود، در این صورت پیادهسازی بازیابی کاربر به ارائه دهنده احراز هویت سپرده میشود.
لاراول دو ارائه دهنده احراز هویت دارد که یکی Database و دیگری Eloquent است. ارائه دهنده احراز هویت Database اقدام به بازیابی مستقیم اطلاعات احراز هویت کاربر از حافظه بکاند میکند، در حالی که Eloquent لایه انتزاعی ارائه میکند که مراحل مورد نیاز را اجرا میکند.
در مثال مورد بررسی این مقاله، ما اقدام به پیادهسازی یک ارائهدهنده احراز هویت MongoDB میکنیم که اطلاعات احراز هویت کاربر را از بکاند MongoDB واکشی میکند.
بدین ترتیب مقدمه کوتاه ما در مورد محافظها و ارائه دهندهها در سیستم احراز هویت لاراول به پایان میرسد. در بخشهای بعدی روی توسعه محافظ و ارائه دهنده احراز هویت سفارشی خود متمرکز خواهیم شد.
در ادامه به بررسی فهرست فایلهایی میپردازیم که در طی این مقاله پیادهسازی خواهیم کرد.
اگر فعلاً از فهرست فایلهای فوق چندان سر در نمیآورید، نباید نگران باشید، زیرا همه این موارد را در ادامه مقاله به تفصیل بررسی خواهیم کرد.
در این بخش به بررسی مراحل پیادهسازی فایلهای مورد نیاز خواهیم پرداخت.
نخستین کاری که باید انجام دهیم این است که در مورد محافظ سفارشی خود به لاراول اطلاع میدهیم. بدین منظور جزییات محافظ سفارشی را در فایل config/auth.php به صورت زیر وارد میکنیم:
چنان که میبینید، محافظ سفارشی خود را زیر کلید Custom اضافه کردهایم. سپس باید یک مدخل ارائهدهنده مرتبط در بخش providers اضافه کنیم:
ما مدخل ارائهدهنده خود را زیر کلید mongo اضافه کردهایم. در نهایت محافظ احراز هویت پیشفرض خود را از «وب» به «سفارشی» عوض میکنیم:
البته این ارائهدهنده هنوز کار نمیکند، زیرا هنوز فایلهای ضروری را پیادهسازی نکردهایم. این همان است که در چند بخش بعدی برسی خواهیم کرد.
در این بخش فایلهایی که برای مکالمه با وهله MongoDB مورد نیاز است را پیادهسازی میکنیم.
ابتدا یک فایل پیکربندی به نام config/mongo.php ایجاد میکنیم که تنظیمات پیشفرض اتصال MongoDB را نگهداری میکند.
البته شما باید مقادیر را با تنظیمات خودتان عوض کنید. ما به جای ایجاد مستقیم یک کلاس که با MongoDB تعامل داشته باشد، در وهله نخست یک اینترفیس میسازیم. مزیت ایجاد یک اینترفیس این است که یک قرارداد ارائه میکند که هر توسعهدهندهای باید در زمان پیادهسازی از آن تبعیت کند. ضمناً پیادهسازی ما از MongoDB میتواند به سادگی درون پیادهسازی NoSQL دیگری قرار گیرد.
در ادامه یک فایل اینترفیس به نام app/Services/Contracts/NosqlServiceInterface.php با محتوای زیر ایجاد میکنیم:
این یک اینترفیس کاملاً ساده است که متدهای ابتدایی CRUD را اعلان میکند و یک کلاس نیز اعلان میکند که در آن این اینترفیس را پیادهسازی میکند.
اینک میتوانیم یک کلاس واقعی در مسیر app/Database/MongoDatabase.php تعریف میکنیم:
البته تصور میکنیم که شما قبلاً MongoDB و اکستنشن MongoDB PHP متناظر را نصب کردهاید.
متد construct__ یک کلاس را با پارامترهای ضروری وهلهسازی میکند. متد مهم دیگری که به آن علاقهمندیم متد find است که رکورد مبتنی بر معیار ارائه شده از سوی آرگومانهای متد را بازیابی میکند. این پیادهسازی درایور MongoDB بود که تلاش کردیم تا حد امکان آن را ساده حفظ کنیم.
ما با پیروی از استانداردهای سیستم احراز هویت مدل User را پیادهسازی میکنیم که باید قرارداد را پیادهسازی کند. در ادامه یک فایل به نام app/Models/Auth/User.php با محتوای زیر ایجاد میکنیم:
احتمالاً تاکنون متوجه شدهاید که App\Models\Auth\User قرارداد Illuminate\Contracts\Auth\Authenticatable را پیادهسازی میکند.
اغلب متدهایی که در کلاس ما پیادهسازی شدهاند کاملاً گویا هستند. مثلاً متد fetchUserByCredentials کاربر را از بکاند موجود بازیابی میکند. در این مثال، بکاند، کلاس MongoDatabase است که برای بازیابی اطلاعات مورد نیز فراخوانی خواهد شد. بدین ترتیب کار پیادهسازی مدل User به پایان میرسد.
چنان که پیشتر اشاره کردیم، سیستم احراز هویت لاراول شامل دو عنصر محافظ و ارائه دهنده است. در این بخش یک ارائه دهنده احراز هویت میسازیم که به منظور بازیابی کاربر از بکاند مورد استفاده قرار میگیرد. در ادامه فایلی به نام app/Extensions/MongoUserProvider.php با محتوای زیر ایجاد میکنیم:
در این مورد نیز باید مطمئن شویم که ارائهدهنده سفارشی حتماً قرارداد Illuminate\Contracts\Auth\UserProvider را پیادهسازی میکند در ادامه دو متد مهم به نامهای retrieveByCredentials و validateCredentials تعریف میکنیم.
متد retrieveByCredentials برای بازیابی احراز هویت با استفاده از کلاس مدل User استفاده میشود که در بخش قبلی ساختهایم. از سوی دیگر متد validateCredentials برای اعتبارسنجی کاربر در برابر مجموعه مفروضی از اطلاعات احراز هویت مورد استفاده قرار میگیرد.
بدین ترتیب کار پیادهسازی ارائه دهنده سفارشی ما نیز به پایان میرسد. در بخش بعدی یک محافظ ایجاد میکنیم که با ارائه دهنده احراز هویت MongoUserProvider تعامل دارد.
چنان که قبلاً بررسی کردیم، محافظ در سیسم احراز هویت لاراول، شیوه احراز هویت کاربر را تدارک میبیند. در مثال عملی مورد بررسی در این مقاله ما اقدام به بررسی پارامتر درخواست jsondata میکنیم که باید شامل رشته اطلاعات احراز هویت باشد که به صورت JSON انکود شده است.
در این بخش یک محافظ ایجاد میکنیم که با ارائهدهنده احراز هویت که در بخش قبل ساختیم تعامل دارد. در ادامه فایلی به نام app/Services/Auth/JsonGuard.php و با محتوای زیر ایجاد میکنیم:
قبل از هر چیز کلاس ما باید اینترفیس 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 را باز میکنیم که امکان افزودن اتصالهای کانتینر سرویس احراز هویت را به ما میدهد. اگر این فایل شامل هیچ تغییر سفارشی نباشد، میتوانید آن را با محتوای زیر تعویض کنید:
در ادامه متد boot را مینویسیم که شامل اغلب اتصالهای ارائهدهنده است. در آغاز کار اتصالهایی برای عناصر App\Database\MongoDatabase و App\Models\Auth\User ایجاد میکنیم.
اینک زمان آن رسیده است که محافظ و ارائهدهنده سفارشی را به سیستم احراز هویت لاراول وصل کنیم. ما از متد Auth Facade ارائهدهنده برای افزودن ارائهدهنده احراز هویت سفارشی خود زیر کلید mongo استفاده میکنیم. به خاطر داشته باشید که این کلید تنظیمات انجام یافته قبلی در فایل auth.php را بازتاب میدهد.
به طور مشابه، پیادهسازی محافظ سفارشی خود را با استفاده از متد extend مربوط به Auth facade تزریق میکنیم.
در ادامه یک متد register وجود دارد که از آن برای اتصال اینترفیس App\Services\Contracts\NosqlServiceInterface interface به پیادهسازی App\Database\MongoDatabase استفاده میکنیم.
بدین ترتیب هر زمان که لازم باشد وابستگی App\Services\Contracts\NosqlServiceInterface برقرار شود، لاراول با پیادهسازی آداپتر App\Database\MongoDatabase پاسخ میدهد.
مزیت استفاده از این رویکرد آن است که فرد میتواند به سادگی پیادهسازی مفروض را با پیادهسازی سفارشی تعویض کند. برای نمونه تصور کنید فردی میخواهد در آینده پیادهسازی App\Database\MongoDatabase را با آداپتر CouchDB عوض کند. در این حالت، کافی است اتصال متناظر را در متد register اضافه کند.
بدین ترتیب ارائهدهنده سرویس آماده به کار شده است. در این زمان ما همه موارد لازم برای تست پیادهسازی محافظ سفارشی خود را در اختیار داریم، بنابراین در بخش بعدی به تست آن میپردازیم.
آیا سیستم ما کار میکند؟ بدین ترتیب ما همه مراحل دشوار محافظ احراز هویت سفارشی اول خود را سپری کردهایم و اینک زمان آن رسیده که از آن استفاده کرده و آن را امتحان کنیم.
در ادامه یک فایل کنترلر ابتدایی به نام app/Http/Controllers/MongoController.php مانند زیر پیادهسازی کردیم:
در ادامه نگاهی دقیق به وابستگی متد login میاندازیم که نیازمند پیادهسازی محافظ Illuminate\Contracts\Auth\Guard است. از آنجا که ما محافظ custom را به عنوان محافظ پیشفرض در فایل auth.php تنظیم کردیم، در واقع این App\Services\Auth\JsonGuard است که تزریق میشود.
سپس متد validate را از کلاس App\Services\Auth\JsonGuard فراخوانی میکنیم که به نوبه خود یک سری از فراخوانیهای متد را دارد:
اگر همه چیز مطابق انتظار اجرا شود، با فراخوانی متد user در محافظ خودمان یک کاربر احراز هویت شده به دست میآوریم.
برای دسترسی به کنترلر باید یک مسیر مرتبط در فایل routes/web.php اضافه کنید.
اگر تلاش کنید بدون هر گونه پارامتری به مسیر http://your-laravel-site/custom/mongo/login دسترسی پیدا کنید، با پیام not authorized مواجه میشوید.
از سوی دیگر، اگر آدرسی مانند آنچه در زیر آمده است را وارد کنید در پاسخ در صورتی که کاربر در پایگاه داده موجود باشد، پیام موفقیت را دریافت خواهید کرد.
http://your-laravel-site/custom/mongo/login?jsondata={“username”:”admin”,”password”:”admin”}
توجه داشته باشید که این موارد صرفاً به عنوان نمونه هستند و قصد ما نمایش طرز کار محافظ سفارشی است. شما در عمل باید از طرز عمل گردش احراز هویت و مسئولیت خود برای ایجاد یک راهحل پایدار و امن برای اپلیکیشن آگاه باشید.
فریمورک لاراول یک سیستم احراز هویت پایدار در هسته مرکزی خود دارد که در صورت نیاز میتواند برای ساخت یک سیستم احراز هویت سفارشی بسط یابد. در این مقاله به بررسی روش پیادهسازی یک محافظ سفارشی و اتصال آن به گردش کار احراز هویت لاراول پرداختیم. در طی این مسیر یک سیستم توسعه دادیم که کاربر بر مبنای یک رشته JSON در درخواست که با پایگاه داده MongoDB تطبیق مییافت، احراز هویت میشود. برای نیل به این مقصود در نهایت یک محافظ سفارشی و یک ارائهدهنده سفارشی را پیادهسازی کردیم.
ا
منبع: فرادرس
در این راهنما نگاهی به الگوریتم مرتب سازی ادغامی و پیادهسازی آن در جاوا خواهیم داشت. مرتبسازی ادغامی یکی از مؤثرترین تکنیکهای مرتبسازی بر مبنای پارادایم «تقسیم و حل» (divide and conquer) است.
مرتبسازی ادغامی یک الگوریتم «تقسیم و حل» است که در آن ابتدا مسئله به مسائل فرعی تقسیم میشود. زمانی که راهحلها برای مسائل فرعی آماده شد، مجدداً آنها را با هم ترکیب میکنیم تا راهحل نهایی برای مسئله اصلی به دست آید.
این یکی از الگوریتمهایی است که با استفاده از «بازگشت» (recursion) به سادگی پیادهسازی میشود، چون به جای مسئله اصلی با مسائل فرعی سر و کار داریم.
الگوریتم آن را میتوان به صورت فرایند 2 مرحلهای زیر توصیف کرد:
نمودار زیر فرایند کامل مرتبسازی ادغامی را برای آرایه نمونه {10, 6, 8, 5, 7, 3, 4} نمایش میدهد.
اگر نگاهی دقیقتر به الگوریتم داشته باشیم، میبینیم که آرایه به صورت بازگشتی به نیمههای متوالی تقسیم میشود تا این که اندازه آن برابر با 1 شود. زمانی که اندازه آن برابر با 1 شد، فرایندهای ادغام وارد عمل میشوند و شروع به ادغام آرایهها برای رسیدن به آرایه مرتب میکنند.

برای پیادهسازی الگوریتم فوق، یک تابع مرتبسازی ادغامی مینویسیم که یک آرایه ورودی و طولانی را به عنوان پارامتر میگیرد. این یک تابع بازگشتی خواهد بود، بنابراین به یک شرایط پایه و بازگشت نیاز داریم.
شرایط پایه در صورتی بررسی میشوند که طول آرایه برابر با 1 باشد، و فقط در این حالت بازگشت مییابد. در بقیه موارد فراخوانی بازگشتی اجرا خواهد شد.
برای حالت بازگشتی اندیس میانه را پیدا کرده و دو آرایه موقت []l و []r را ایجاد میکنیم. سپس تابع mergeSort به صورت بازگشتی برای هر دو آرایه فرعی فراخوانی میشود:
بعد تابع merge را فراخوانی میکنیم که در ورودی خود هر دو آرایه فرعی و اندیسهای آغاز و پایان هر دو آرایه فرعی را میگیرد. تابع merge عناصر هر دو آرایه فرعی را یک به یک مقایسه میکند و عنصر کوچکتر را درون آرایه ورودی قرار میدهد.
زمانی که به انتهای یک آرایه فرعی برسید، بقیه عناصر از آرایه دیگر درون آرایه کپی میشوند و بدین ترتیب آرایه مرتبشده نهایی به دست میآید:
تست unit برنامه به صورت زیر است:
از آنجا که مرتبسازی ادغامی یک الگوریتم بازگشتی است، پیچیدگی زمانی آن را میتوان به صورت رابطه بازگشتی زیر بیان کرد:
(2T(n/2 متناظر با زمان مورد نیاز برای مرتبسازی آرایههای فرعی و زمان (O(n برای ادغام آرایه کلی است. زمانی که مسئله حل شود، پیچیدگی زمانی به (O(nLogn میرسد.
این پیچیدگی برای بدترین حالت، حالت میانگین و بهترین حالت درست است زیرا همواره آرایه را به دو نیمه تقسیم کرده و سپس ادغام میکند. پیچیدگی فضایی الگوریتم نیز (O(n است، چون آرایههای موقتی در هر فراخوانی بازگشتی ایجاد میشوند.
در این راهنمای کوتاه، با طرز کار الگوریتم مرتبسازی ادغامی و روش پیادهسازی آن در جاوا آشنا شدیم. همه کدهای این راهنما را میتوانید در این صفحه گیتهاب (+) ملاحظه کنید.
منبع: فرادرس
پلتفرم اندروید بازه وسیعی از ویجتهای رابط کاربری را ارائه کرده است که برای نیازهای اغلب اپلیکیشنها کافی هستند. این ویجتها عالی هستند و محصولات نهایی کاملاً کاربردی و زیبایی را میسازند؛ اما در پارهای موارد توسعهدهندگان نرمافزار تمایل دارند فراتر از این بیندیشند و رابطهای سفارشی خاص خود را بسازند. بهترین روش برای پاسخ دادن به این خلاقیت، ساخت نمای سفارشی است.
در این نوشته به تعریف و برسی نماهای سفارشی و سپس بخش جذابتر یعنی نمایش آنها خواهیم پرداخت.
در آغاز باید برخی اصطلاحهای مقدماتی را برای درک بهتر تعریف کنیم.
نمای اندروید یا Android View یک کلاس مبنا برای ساخت رابط کاربری است که به توسعهدهندگان فرصتی برای ایجاد طراحیهای پیچیده میدهد. این نما ناحیهای مستطیلی روی صفحه اشغال میکند و مسئول اندازهگیری، طرحبندی و ترسیم خود در راستای عناصر فرزندش است. به علاوه نماها همه ورودیهای کاربر را مدیریت میکنند.

یک گروه نما یا ViewGroup، نمای خاصی است که توانایی گنجاندن نماهای دیگر (فرزندان) را در خود دارد و مشخصات طرحبندی خاص خود را تعریف میکند. این نما همچنین محلی است که هر نمای فرعی (subview) میتواند از آن مشتق شود.
هر نمایی که خارج از ویجت مبنای اندروید ایجاد شود را میتوان یک نمای سفارشی یا Custom View دانست. در این نوشته کل توجه ما روی این نمای سفارشی است.
روشهای بسیار مختلفی برای پیادهسازی نماهای سفارشی وجود دارند و رویکردی که انتخاب میشود به نیازهای شما بستگی دارد. در ادامه برخی روشها را مورد بررسی قرار میدهیم:
این روش زمانی که حجم بالایی از کد راهاندازی برای نمای شما لازم باشد و بخواهید از آن در جاهای مختلفی استفاده کنید مناسب خواهد بود. برای اجتناب از درج کد شلوغ درون اکتیویتی یا فرگمان، میتوانید ویجت مبنا را بسط داده و همه کارهای راهاندازی را درون سازنده انجام دهید، از این رو میتوان به سادگی از آن مجدداً استفاده کرد. این روش به طور بدیهی سادهترین رویکرد برای پیادهسازی نماهای سفارشی محسوب میشود.
اگر میخواهید ابتکار به خرج بدهید و همه کارها را از صفر خودتان اجرا کنید، این روش مناسب خواهد بود. در این روش شما همه منطق رفتاری ویجت را خودتان رسم، اندازهگیری و برنامهریزی میکنید.
در پارهای اوقات مجموعهای از ویجتها دارید که میخواهید آنها را با هم گروهبندی کنید تا یک نمای کاملاً جدید ایجاد کنید. برای نمونه فرض کنید یک Textview و یک Button دارید و میخواهید آنها را درون یک LinearLayout گروهبندی کنید. این نما عموماً به نام «نمای ترکیبی» (Compound View) شناخته میشود. مزیتهای این کار به شرح زیر هستند:
در این بخش به توضیح روش اندروید برای رسم نماها میپردازیم. در آغاز سه مرحله وجود دارند که پیش از نمایش نهایی نما روی صفحه باید اجرا شوند. این سه مرحله، اندازهگیری، طرحبندی و رسم هستند. هر یک از مراحل به صورت پیمایش «عمق-اول» (depth-first) سلسلهمراتب نما است که از والد به سمت فرزند حرکت میکند. در هر مرحله متدی وجود دارند که میتواند بسته به نیازها override شده و تغییر یابد. این فرایند را میتوان به دو مرحله تقسیم کرد:

در این مرحله، این فرصت را داریم که به سیستم اندروید اعلام کنیم میخواهیم اندازه نمای سفارش چه قدر باشد و این اندازه به محدودیتهای تعیین شده از سوی والد بستگی دارد.
نمودار شمارهگذاری شده زیر شیوه اندازهگیری هر نما را در هر گام نمایش میدهد:

نمای فرزند اقدام به تعریف LayoutParams به صورت برنامهنویسی شده و یا در XML میکند و والد این مقادیر را با استفاده از متد ()getLayoutParams بازیابی میکند.
والد، MeasureSpecs را محاسبه کرده و آن را با استفاده از ()child.measure در سلسلهمراتب به سمت پایین ارسال میکند. Measurespecs شامل حالت و مقدار است.
سه حالت برای اندازهگیری وجود دارند:
متد ()onMeasure به همراه پارامترهای MeasureSpecs فراخوانی میشود. در این متد View اقدام به محاسبه عرض/ارتفاع مطلوب میکند و آن را با استفاده از setMeasuredDimension تنظیم میکند. به خاطر داشته باشید که متد setMeasuredDimension باید درون measure فراخوانی چون در غیر این صورت موجب بروز استثنای زمان اجرا میشود.
مرحله بعد و آخر مرحله طرحبندی است. در این مرحله، والد ()child.layout را فراخوانی کرده و اندازه نهایی و موقعیت فرزند را تعیین میکند. زمانی که نمای سفارشی را پیادهسازی میکنید، در صورتی که نمای شما، نماهای فرعی دیگری داشته باشد، باید تنها متد ()onLayout را override کنید.
در نتیجه فرایند اندازهگیری مانند یک مذاکره بین یک والد و فرزند است. فرزند، عرض و ارتفاع مطلوب را محاسبه میکند اما والد کسی است که فراخوانی نهایی را برای تنظیم موقعیت و اندازه فرزندش انجام میدهد.

آخرین و مهمترین مرحله در رسم نمای سفارشی override کردن متد ()onDraw است. بوم یک کلاس مبنا است که متدهای زیادی برای رسم متن، bitmap-ها، خطوط و دیگر اشکال ابتدایی گرافیکی عرضه کرده است.
هر والد، خود را رسم میکند و سپس تقاضا میکند که هر فرزند نیز همان کار را انجام دهد. یک اثر جنبی جالب در زمانی است که والد ابتدا خود را رسم میکند و در انتها فرزندانش را روی خود رسم کرده و در واقع خود را میپوشاند.
اکنون نوبت بخشی رسیده است که مدتها منتظر آن بودیم و آن کدنویسی است. در ادامه به بررسی روش ایجاد یک نمای سفارشی با استفاده از «کاتلین» (Kotlin) میپردازیم. ما جهت مقاصد آموزشی یک شاخص باتری رسم میکنیم تا وضعیت کنونی باتری دستگاه را نمایش دهیم. نمودار زیر وضعیتهای مختلف یک باتری را نشان میدهد:

برای ایجاد یک نمای BatteryMeterView باید مراحل زیر را طی کنیم. یک پروژه اندروید استودیو ایجاد کرده و یک کلاس جدید به نام BatteryMeterView اضافه کنید.
آن را با استفاده از کلاس View بسط دهید و سازندهها را طوری اضافه کنید که با super مطابقت داشته باشند.
ما برای آمادهسازی رسم برخی شیءهای paint، رنگ و شکلها را اعلان میکنیم.
View-ی ما همانند یک ویجت ابتدایی باید مقداری آمادهسازی اولیه داشته باشد و همه مشخصهها نوعی مقدار پیشفرض داشته باشند. در ادامه یک شیء همراه درون BatteryMeterView ایجاد کرده و برخی ثابتها را به آن اضافه میکنیم.
پیش از رسم باتری روی صفحه، باید اندازه و موقعیت آن را بهروزرسانی کنیم. بهترین مکان برای مدیریت هر گونه تغییر اندازه درون متد onSizeChanged است. به این منظور مراحل زیر را طی کنید:
نکته: با توجه به مقاصد آموزشی این مقاله از برخی مقادیر ثابت برای حاشیه و آفست محتوا استفاده کردهایم.
اینک برای رسم BatteryMeter شروع به override کردن متد ()onDraw میکنیم.
به خاطر بسپارید که متد onDraw با بسامد 60 بار در ثانیه (fps 60) فراخوانی میشود و قرار دادن هر نوع عملیات محاسباتی سنگین و ایجاد شیء درون آن میتواند موجب کاهش عملکرد اپلیکیشن شود. برای اجتناب از این وضعیت، میتوانیم همه اشیا را درون سازندهها بسازیم و در صورت نیاز میتوانیم مشخصهها را در ادامه تغییر دهیم.
اکنون برای این که به باتری خود امکان تغییر در زمان اجرا بدهیم، باید متد ()invalidate را هر بار که حالت نما بهروزرسانی میشود فراخوانی کنیم. کاری که invalidate انجام میدهد این است که به اندروید اجازه میدهد بداند که نما خراب شده و نیاز به ترسیم مجدد دارد. لازم به ذکر است که باید مراقب باشید زیرا فراخوانی ()invalidate به تعداد زیاد موجب بروز مشکلاتی میشود.
مرحله نهایی، افزودن نمای باتری به Layout است:
به این ترتیب کار به پایان میرسد و ما اینک یک باتری سنج داریم که خودمان ایجاد کردهایم. برای مشاهده کد کامل پروژه به این لینک (+) مراجعه کنید.
پیش از جمعبندی این مقاله بهتر است مزایا و معایب پیادهسازی نماهای سفارشی را نیز مورد اشاره قرار دهیم. همانند هر فرایند پیادهسازی دیگری، همواره مزیتها و معایبی وجود دارند، اما این موارد نباید شما را از امتحان کردن آن نا امید کنند.
ظاهر/رفتار سفارشی: نمای سفارشی = سفارشیسازی. پلتفرم اندروید گسترده است، اما موارد خاصی وجود دارند که قابلیتهای نماهای اندروید نیازهای شما را برآورده نمیسازند و از این رو نمای سفارشی این فرصت را به شما میدهد که چیزی بسازید که متعلق به شما است. زمانی که نوبت به طراحی و تعامل میرسد، کنترل کاملی دارید زیرا نمای سفارشی گزینههای بینهایتی ارائه میکند.
قابلیت استفاده مجدد/قابلیت خوانایی: زمانی که اپلیکیشنهای بزرگمقیاس توسعه میدهید، قابلیت استفاده مجدد کد همواره موضوعی مورد علاقه است. زمانی که یک نمای سفارشی ایجاد میکنید، میتوانید به سادگی آن را در چند جای دیگر نیز در اپلیکیشن خود مورد استفاده قرار دهید.
عملکرد: در برخی موارد خاص، ساختن نمای سفارشی میتواند موجب مقداری بهبود عملکرد شود.
در این بخش برخی معایب نماهای سفارشی را بررسی میکنیم.
زمان و تلاش: ایجاد نماهای سفارشی کاری زمانبر است و استفاده از آن قطعاً میتواند دشوار باشد تا اینکه به کارکرد آنها آشنا شوید.
پشتکار: چند چیز وجود دارند که در زمان پیادهسازی نماهای سفارشی باید آگاه باشید. اول این که باید مطمئن باشید فونت، اندازه متن، رنگ، سایهها، هایلایت و استایل را به درستی مدیریت کردهاید. همچنین باید مطمئن شوید که نما روی همه تراکمهای نمایشی به درستی کار میکند چون زیرکلاس بوم اندروید برحسب پیکسل رسم میشود و نه DP. اگر با تصاویر کار میکنید باید به خاطر داشته باشید که نسبت تصویر، بزرگنمایی و مقیاسبندی درستی داشته باشد.
موارد زیادی که باید مدیریت شوند: همچنین باید همه انواع شنوندههای کلیک، تعاملهای کاربر، کلیکهای منفرد، دو بار کلیک، فشردن طولانی، سوایپ کردن و جابجایی را مدیریت کنید.
در این مقاله به بررسی اتفاقاتی میپردازیم که در جریان تحویل یک فرم از سوی کاربر رخ میدهد. در این نوشته بررسی خواهیم کرد که در زمان ارسال فرم HTML دادههای آن به کجا میروند و زمانی که به مقصد رسیدند چگونه میتوانیم آنها را مدیریت کنیم. همچنین به بررسی برخی دغدغههای امنیتی مرتبط با ارسال داده های فرم میپردازیم. برای مطالعه بخش قبلی این سری مطالب به لینک زیر رجوع کنید:
پیشنیاز مطالعه این نوشته سواد مقدماتی رایانه، داشتن درکی از HTML و دانش ابتدایی HTTP و همچنین برنامهنویسی سمت سرور است. هدف از این مقاله درک اتفاقاتی است که در زمان ارسال فرم رخ میدهد. بدین ترتیب ایدهای ابتدایی از شیوه پردازش دادهها در سمت سرور به دست میآوریم.
در این بخش بررسی میکنیم که وقتی دکمه ارسال فرم کلیک میشود، دادههای فرم HTML به کجا میروند؟
مفهوم وب مبتنی بر یک معماری کاملاً ابتدایی کلاینت/سرور است که آن را میتوان به صورت زیر خلاصه کرد: یک کلاینت (معمولاً مرورگر وب) درخواستی را با استفاده از پروتکل HTTP به سرور (در اغلب موارد یک وبسرور مانند Apache، Nginx ،IIS ،Tomcat و غیره) ارسال میکند. سرور با استفاده از همان پروتکل به درخواست پاسخ میدهد.

در سمت کلاینت، فرم HTML چیزی به جز روشی کاربرپسند آسان برای پیکربندی یک درخواست HTTP جهت ارسال دادهها به سرور نیست. بدین ترتیب کاربر میتواند اطلاعاتی را در درخواست HTTP تحویل دهد.
عنصر <form> شیوه ارسال دادهها را تعریف میکند. همه خصوصیتهای آن طوری طراحی شدهاند که امکان پیکربندی درخواست برای ارسال شدن در زمان کلیک روی دکمه Submit را فراهم سازند. دو مورد از مهمترین خصوصیتها action و method هستند.
این خصوصیت شیوه ارسال دادهها را تعریف میکند. مقدار آن باید یک URL معتبر باشد. اگر این خصوصیت ارائه نشده باشد، دادهها به URL صفحهای که شامل فرم است ارسال خواهند شد.
در این مثال، دادهها به یک URL مطلق یعنی (http://foo.com) ارسال میشوند:
در مثال زیر از یک URL نسبی برای ارسال دادهها به URL متفاوتی روی سرور استفاده شده است:
زمانی که مانند مثال زیر، هیچ خصوصیتی ذکر نشود، دادههای <form> به همان صفحهای ارسال میشوند که فرم در آن قرار دارد:
صفحههای قدیمیتر زیادی از نمادگذاری زیر استفاده میکنند تا نشان دهند که دادهها باید به همان صفحهای ارسال شوند که شامل فرم است. دلیل الزامی بودن این وضعیت آن است که تا HTML5 خصوصیت action الزامی بود، اما دیگر نیازی به آن ندارید.
نکته: میتوان از یک URL استفاده کرد که از پروتکل HTTPS یعنی HTTP امن بهره میگیرد. زمانی که این کار را انجام دهید، حتی اگر خود فرم روی یک صفحه ناامن میزبانی شده باشد و از طریق HTTP به آن دسترسی داشته باشید، دادهها همراه با باقی درخواست رمزنگاری میشوند. از سوی دیگر، اگر فرم روی یک صفحه امن میزبانی شده باشد، اما یک URL ناامن به صورت HTTP برای خصوصیت action وارد کنید، همه مرورگرها زمانی که کاربر دادهها را ارسال میکند، هشداری نمایش میدهند، زیرا دادهها رمزنگاری نشدهاند.
این خصوصیت شیوه ارسال شدن دادهها را تعریف میکند. پروتکل HTTP چندین روش برای اجرای درخواست ارائه میکند. دادههای فرم HTML از طریق چند روش مختلف ارسال میشوند که رایجترین آنها متد GET و متد POST هستند.
برای درک تفاوت بین دو متد فوق باید گامی به عقب برداریم و طرز کار HTTP را بررسی کنیم. هر بار که میخواهید به منبعی روی وب دسترسی پیدا کنید، مرورگر یک درخواست به آن URL ارسال میکند. هر درخواست HTTP شامل دو بخش است که یکی هدر و دیگری بدنه درخواست است. بخش هدر شامل مجموعهای از متادیتا کلی در مورد قابلیتهای مرورگر است و بخش بدنه میتواند شامل آن دسته از اطلاعات ضروری باشد که سرور برای پردازش یک درخواست خاص به آنها نیاز دارد.
متد GET از سوی مرورگر برای درخواست از سرور جهت بازگشت دادن منبع مفروض مورد استفاده قرار میگیرد. در این حالت مرورگر یک بدنه خالی ارسال میکند. از آنجا که بدنه درخواست خالی است، اگر فرم با استفاده از این متد ارسال شود، دادههای ارسالی به سرور به URL الحاق میشوند.
فرم زیر را در نظر بگیرید:
از آنجا که متد GET مورد استفاده قرار گرفته است، میبینید که در زمان تحویل دادن فرم، URL-ای به صورت زیر در نوار آدرس مرورگر ظاهر میشود.
www.foo.com/?say=Hi&to=Mom

دادههای الحاقی به URL به صورت یک سری از جفتهای نام/مقدار هستند. پس از آن که آدرس وب URL پایان یافت یک کاراکتر علامت سؤال (?) و سپس جفتهای نام/مقدار را میآوریم که هر کدام با یک کاراکتر & از هم جدا میشوند. در این مورد، ما دو بخش از دادهها را به سرور ارسال میکنیم:
بدین ترتیب درخواست HTTP به صورت زیر خواهد بود:
متد POST کمی متفاوت است. این همان متدی است که مرورگر از آن برای صحبت کردن با سرور هنگام درخواست یک پاسخ استفاده میکند. چنین درخواستی با در نظر گرفتن دادههای ارائه شده در بدنه درخواست HTTP ارسال میشود. به زبان ساده درخواست بیان میکند: «سلام سرور، به این دادهها نگاه کن و یک نتیجه مناسب به من بازگشت بده.» اگر فرم با استفاده از این متد ارسال شود، دادهها به بدنه درخواست HTTP الحاق میشوند.
زمانی که فرم با استفاده از متد POST ارسال میشود، هیچ دادهای به URL الحاق نمیشود، و درخواست HTTP نیز به صورت زیر به نظر میرسد و به جای آن دادهها در بدنه درخواست HTTP قرار میگیرند:
هدر Content-Length نشاندهنده اندازه بدنه است و هدر Content-Type نوع منابعی که به سرور ارسال میشود را نشان میدهد. در ادامه در مورد این هدرها بیشتر صحبت خواهیم کرد.
درخواستهای HTTP هرگز به کاربر نمایش پیدا نمیکنند، اگر میخواهید آنها را مشاهده کنید باید از ابزارهایی مانند Network Monitor در فایرفاکس یا Developer Tools در کروم بهره بگیرید. به عنوان مثال، دادههای فرم به صوت زیر در برگه Network کروم نمایش پیدا میکنند. پس از تحویل دادن فرم:
شما میتوانید دادههای فرم را به صورتی که در تصویر زیر نمایش یافته به دست آورید:

تنها چیزی که برای کاربر نمایش پیدا میکند، URL فراخوانیشده است. چنان که پیشتر اشاره کردیم، با یک درخواست GET کاربر دادهها را در نوار URL خود میبیند، اما با درخواست POST چنین اتفاقی نمیافتد. این وضعیت به دو دلیل میتواند مهم باشد.
اگر بخواهید یک رمز عبور (یا هر داده حساس دیگر) را ارسال کنید، هرگز نباید از متد GET استفاده کنید، چون در غیر این صورت خطر نمایش یافتن آن در نوار URL وجود دارد که کاری بسیار غیر امن است.
اگر میخواهید حجم بالایی از دادهها را ارسال کنید، متد POST ترجیح دارد، زیرا، مرورگرها محدودیت اندازه URL را دارند و اغلب سرورها نیز برای پذیرش URL محدودیت تعیین کردهاند.
هر نوع متد HTTP که انتخاب کنید، سرور رشتهای متنی دریافت خواهد کرد که آن را تجزیه میکند تا دادههایش را به صورت لیستی از جفتهای کلید/مقدار به دست آورد. روش دسترسی به این لیست به پلتفرم توسعه یا فریمورک خاصی که مورد استفاده قرار میدهید مرتبط خواهد بود. آن فناوری که استفاده میکنید روی روش مدیریت کلیدهای تکراری نیز تأثیر دارد. در اغلب موارد مقداری که اخیراً برای یک کلید، فعال بوده است ترجیح دارد.
PHP برخی اشیای سراسری برای دسترسی به دادهها ارائه میکند. با فرض این که از متد POST استفاده میکنید، مثال زیر صرفاً دادهها را میگیرد و آنها را به کاربر نمایش میدهد. البته این که با دادهها چه میکنید به شما مربوط است. میتوان دادهها را نمایش داد، آنها را در پایگاه داده ذخیره کرد، به وسیله ایمیل ارسال نمود یا به روش دیگری مورد پردازش قرار داد.
مثال زیر صفحهای را به همراه دادههای ارسالی نمایش میدهد. در فایل زیر روش استفاده عملی از آن را مشاهده میکنید:
فایل فوق شامل همان مثالی است که قبلاً دیدیم و دارای متد POST و یک action به صورت php-example.php است. زمانی که این فرم تحویل میشود، دادهها را به فایل php-example.php ارسال میکند که شامل کد PHP است که در قطعه کد فوق مشاهده میکنید. زمانی که این کد اجرا شود، خروجی مرورگر به صورت Hi Mom خواهد بود.

نکته: مثال فوق زمانی که صفحه را به صورت لوکال در مرورگر بارگذاری کنید کار نمیکند، زیرا مرورگرها نمیتوانند کد PHP را تفسیر کنند، بنابراین وقتی که فرم تحویل میشود، مرورگر صرفاً پیشنهاد میکند که فایل PHP را دانلود کنید. برای این که این مثال عملیاتی شود، باید آن را روی نوعی سرور PHP اجرا کنید. گزینههای مناسب برای تست PHP نرمافزارهای MAMP و AMPPS هستند.
مثال زیر شیوه استفاده از پایتون برای انجام کاری مشابه فوق را نمایش میدهد. بدین ترتیب دادههای تحویلی در یک صفحه وب نمایش پیدا میکنند. در این مثال از فریمورک Flask برای رندر کردن قالبها، مدیریت تحویل دادهها و موارد دیگر استفاده شده است:
دو قالبی که در کد فوق مورد ارجاع قرار گرفتهاند به شرح زیر هستند:
نکته: این کد نیز در صورتی که آن را مستقیماً در مرورگر بارگذاری کنید کار نخواهد کرد. طرز کار پایتون اندکی با PHP متفاوت است. برای این که بتوانید آن را به صورت لوکال اجرا کنید باید Python/PIP را نصب کنید. سپس باید Flask را با استفاده از دستور زیر نصب کنید:
pip3 install flask
در ادامه باید مثال را با استفاده از دستور زیر اجرا کنید:
python3 python-example.py
سپس به آدرس localhost:5000 در مرورگر خود بروید.
فناوریهای سمت سرور زیاد دیگری نیز وجود دارند که میتوانید برای مدیریت فرم استفاده کنید و شامل Perl ،Java ،.Net ،Ruby و غیره هستند. شما میتوانید هر کدام را که دوست دارید انتخاب کنید. البته لازم به ذکر است که استفاده مستقیم از این فناوریها بسیار نامعمول است، زیرا کار با آنها کمی دشوار است. به طور معمول افراد از فریمورکهای خوبی مانند لیست زیر که وجود دارند استفاده میکنند تا مدیریت فرمها را به روش آسانتر انجام دهند:
لازم به ذکر است که حتی در صورت استفاده از این فریمورکها نیز، کار با فرمها لزوماً آسان نیست. اما به هر حال نسبت به تلاش برای نوشتن همه کارکردها از صفر بسیار آسانتر است و صرفهجویی زمانی زیادی ایجاد میکند.
نکته: آموزش همه زبانها یا فریمورکهای سمت سرور خارج از حیطه این مقاله است.
ارسال فایلها با استفاده از فرمهای HTML یک حالت خاص است. فایلها دادههای باینری هستند یا دست کم این گونه تصور میشوند، در حالی که همه دادههای دیگر متنی هستند. از آنجا که HTTP یک پروتکل متنی است، الزامات خاصی برای مدیریت دادههای باینری وجود دارند.
این خصوصیت امکان تعیین مقدار هدر HTTP به صورت Content-Type را میدهد که در درخواست ایجاد شده هنگام تحویل فرم جای میگیرد. این هدر بسیار مهم است، زیرا به سرور اعلام میکند که چه نوع دادههایی ارسال میشوند. مقدار آن به صورت پیشفرض به صورت زیر است:
application/x-www-form-urlencoded
به زبان ساده معنای آن چنین است که: «این یک داده فرم است که به صورت پارامترهای URL کدگذاری شده است.»
اگر بخواهید فایلها را ارسال کنید، باید گامهای بیشتری بردارید:
برای نمونه:
نکته: برخی مرورگرها از خصوصیت multiple روی عنصر <input> پشتیبانی میکنند و بدین ترتیب میتوانید بیش از یک فایل را به وسیله تنها یک عنصر <input> برای آپلود انتخاب کنید. این که سرور چگونه این فایلها را در عمل مدیریت میکند، به فناوری مورد استفاده در سمت سرور بستگی دارد. چنان که پیشتر گفتیم، استفاده از یک فریمورک موجب میشود که کارها بسیار آسانتر شوند.
هشدار: بسیاری از سرورها با محدودیت اندازه برای فایلها و درخواستهای HTTP پیکربندی شدهاند تا از سوءاستفادههای احتمالی جلوگیری کنند. پیش از ارسال یک فایل، این محدودیت را با مدیر سرور بررسی کنید.
هر بار که دادهها را به سرور ارسال میکنید، باید بحث امنیت را در نظر داشته باشید. فرمهای HTML با اختلاف زیاد یکی از مهمترین هدفهای حملات رایج به سرورها هستند. البته این مشکلات هرگز از خود فرمهای HTML ناشی نمیشوند، بلکه ناشی از شیوه مدیریت دادهها از سوی سرور هستند.
بسته به این که میخواهد چه کار بکنید، برخی مشکلات کاملاً شناختهشده امنیتی وجود دارند که ممکن است با آنها مواجه شوید:
اسکریپتنویسی بین سایت (XSS) و جعل درخواست بین سایت (CSRF) دو نوع رایج از حملههایی هستند که در زمان نمایش دادههای ارسالی یک کاربر به کاربر دیگر رخ میدهند.
حمله XSS به مهاجم امکان میدهد که اسکریپتهای سمت کلاینت را در صفحههای وب مشاهدهشده از سوی کاربران دیگر تزریق کند. آسیبپذیری اسکریپتنویسی بین سایت از سوی مهاجمان برای دور زدن کنترلهایی مانند «سیاست ریشه یکسان» (same origin policy) را فراهم میسازد. تأثیر این حملهها متفاوت است و از یک مزاحمت ساده تا ریسکهای امنیتی مهم متغیر خواهد بود.
حملههای CSRF مشابه حملههای XSS هستند، چون به روش مشابهی آغاز میشوند و اسکریپت سمت کلاینت در صفحههای وب تزریق میشود، اما هدف آنها متفاوت است. مهاجمان CSRF تلاش میکنند تا دسترسیهای مربوط به کاربران با دسترسی بالاتر (مانند مدیر سایت) را به دست آورند تا اقداماتی را که نباید انجام دهند به اجرا درآورند. برای مثال بدین ترتیب میتوانند دادهها را به یک کاربر غیر معتمد ارسال کنند.
حملههای XSS از اعتمادی که یک کاربر به وبسایت دارد سوءاستفاده میکنند، در حالی که حملههای CSRF از اعتمادی که وبسایت به کاربران خود دارد سوءاستفاده میکنند.
برای جلوگیری از این حملهها، باید همواره دادههایی که یک کاربر به سرور ارسال میکند (در صورت نیاز به نمایش دادن) را بررسی کنید و تلاش کنید محتوای HTML ارائهشده از سوی کاربر را نمایش ندهید. به جای آن باید دادههای ارائهشده از سوی کاربر را پردازش کنید تا به صورت خام نمایش پیدا نکند. تقریباً همه فریمورکهای موجود امروزه یک فیلتر کمینه پیادهسازی کردهاند که عناصر <script> ،<iframe> و <object> مربوط به HTML را از دادههای ارسالی از سوی هر کاربر حذف میکنند. بدین ترتیب ریسک کاهش پیدا میکند، اما لزوماً رفع نمیشود.
تزریق SQL نوعی از حمله است که در آن مهاجم تلاش میکند اقدامی را روی یک پایگاه داده مورد استفاده از سوی وبسایت هدف اجرا نماید. این نوع حمله عموماً شامل ارسال یک درخواست SQL به امید اجرا شدن آن از سوی سرور است. این اتفاق معمولاً زمانی رخ میدهد که سرور اپلیکیشن تلاش میکند دادههای ارسالی از سوی کاربر را در پایگاه داده ذخیره کند. در واقع این نوع حمله یکی از اصلیترین نوع حملات در برابر وبسایتها محسوب میشود.
عواقب چنین حملاتی میتواند فاجعهآمیز باشد و از فقدان دادهها تا حملاتی برای به دست آوردن کنترل زیرساخت وبسایت از طریق کسب دسترسیهای مورد نیاز متفاوت است. این یک تهدید بسیار جدی است و شما هرگز نباید دادههای ارسالی از سوی کاربر را بدون اجرای نوعی مراحل پاکسازی در پایگاه داده ذخیره کنید.
این نوع از حملهها زمانی رخ میدهند که اپلیکیشن هدرهای HTTP یا ایمیلها را بر مبنای دادههای وارد شده از سوی کاربر در یک فرم میسازد. این حمله به صوت مستقیم به سرور شما آسیب نمیزند و کاربران را نیز متأثر نمیسازد، اما یک درِ باز برای مشکلات عمیقتری مانند سرقت «نشست» (Session) یا حملههای فیشینگ فراهم میکند.
این نوع حملهها عموماً خاموش هستند و میتوانند سرور شما را به یک زامبی تبدیل کنند.
اینک سؤال این است که چگونه میتوان با این تهدیدها مبارزه کرد؟ پاسخ دادن به این سؤال نیاز به یک راهنمای کاملاً مفصلی دارد، اما به طور خلاصه چند قاعده وجود دارد که باید در ذهن خود داشته باشید. مهمترین قاعده این است که هرگز به کاربرانتان که شامل خود شما نیز میشود اعتماد نکنید. حتی یک کاربر معتمد هم میتواند به سرقت رفته باشد.
همه دادههایی که به سرور میآیند باید بررسی و پاکسازی شوند. هیچ استثنایی در این خصوص وجود ندارد.
با پیروی از سه قاعده فوق از بسیاری از مشکلات امنیتی که ممکن است پیش آید جلوگیری میکنید، اما اجرای یک بررسی امنیتی از سوی شخص ثالث همواره ایده خوبی تلقی میشود. همواره این را در نظر داشته باشید که شما شخصاً نمیتوانید همه مشکلات بالقوه را ببینید.
چنان که مشاهده کردید، ارسال دادههای فرم کار آسانی است، اما امن سازی یک اپلیکیشن چنین نیست. کافی است به خاطر بسپارید که یک توسعهدهنده فرانتاند تنها کسی نیست که مدل امنیتی دادهها را تعریف میکند. چنان که در این مقاله دیدیم امکان اجرای اعتبارسنجی دادهها در سمت کلاینت وجود دارد، اما سرور نمیتواند به این اعتبارسنجی اعتماد کند، زیرا هیچ راهی برای دانستن این که در سمت کلاینت چه اتفاقی رخ داده است ندارد.