در اینجا غیر از مورد زمانبندی انجام پروژه سعی شده است به دیگر موارد غیره از قبیل شناخت نیازمندیها، نحوه بستن قرارداد، نحوه قیمت دهی و ... اشاره نشود.
در ابتدا در مورد موضوعات کلی و عمومی بحث میکنیم.
1- انتخاب فریمورک، فریمورکهای فراوان و مختلفی برای کار با زمینههای مختلف نرم افزاری در جهان وجود دارند که هرکدام مزایا و معایبی دارند. این روزها استفاده از فریمورکها به قدری جای افتاده است و به اندازهای امکانات دارند که حتی ممکن است امکانات یک فریم ورک باعث شود از یک زبانی که در تخصصتان نیست استفاده کنید و آنرا یاد بگیرید.
2- زمانبندی انجام پروژه، به نظر خود بنده، سختترین و اساسیترین مرحله، برای هر پروژهای، زمانبندی مناسب آن است که نیازمندی اساسی آن، شناخت سایر مواردی است که در این متن بدانها اشاره میشود. زمانبندی دقیق، قرار ملاقاتها و تحویل بهموقع پیش نمایشهای نرم افزار، ارتباط مستمر با کارفرما و تحویل حتی زودتر از موعد پروژه باعث رضایت بیشتر کارفرما و حس اطمینان بیشتر خواهد شد. اگر در تحویل پروژه دیرکرد وجود داشته باشد، باعث دلسردی کارفرما و نوعی تبلیغ منفی خواهد بود. حتی زمانبندی و تحویل به موقع پروژه برای کارفرما بیشتر از کیفیت اهمیت دارد.
3- انتخاب معماری نرم افزار، معماری نرم افزار در اصل تعیین کننده نحوه قطعه بندی و توزیع تکههای نرم افزار، نحوه ارتباط اجزاء،، قابلیت تست پذیری، قابلیت نگهداری و قابلیت استفاده مجدد از کدهای تولید شده میباشد. یکی از اهداف اساسیای که باید در معماری نرمافزار بدان توجه کرد، قابلیت استفاده مجدد از کد است. در یک معماری خوب ما قطعاتی درست خواهیم کرد که بهراحتی میتوانیم از آن در نرمافزارهای دیگر نیز استفاده کنیم. البته قابلیت تست پذیری و قابلیت نگهداری نیز حداقل به همان اندازه اهمیت دارند. در این سایت موارد بسیار زیاد و کاملی جهت ساخت معماری مناسب و design patterns وجود دارد که میتوانید در اینجا یا اینجا مشاهده کنید.
4- قابلیت اجرا بر روی پلتفرمهای مختلف، هرچند این مورد ممکن است بیشتر به نظر کارفرما بستگی داشته باشد، اما در کل اگر کارفرما بتواند سیستم را در پلتفرمهای مختلفی اجرا کند، راضیتر خواهد شد. اگر قصد فروش نرمافزار طراحی شده را داشته باشیم، در اینصورت نیز میتوانیم کاربران پلتفرمهای مختلف را مورد هدف قرار دهیم یا سیستم را در سرورهای مختلفی میزبانی کنیم.
5- انتخاب سیستم بانک اطلاعاتی و نحوه ارتباط با آن. باید تصمیم بگیرید که از چند سیستم بانک اطلاعاتی، چگونه و به چه منظوری استفاده خواهید کرد. مواردی وجود دارند که سیستم را طوری طراحی کردهاند تا در زمان بهره برداری امکان انتخاب بانکهای اطلاعاتی یا نحوه ذخیره اطلاعات برای مدیر سیستم وجود دارد. مثلا در BlogEngine.net میتوان انتخاب کرد که اطلاعات در SQL Server ذخیره شوند یا در سیستم فایل مبتنی بر XML . بحثهای بسیار زیادی در این سایت و کل فضای وب پیرامون نحوه انتخاب و استفاده از ORM ها، چگونگی معماری مناسب آن وجود دارد. بطور مثال همیشه بحث سر اینکه از الگوی Repository استفاده شود یا نشود وجود دارد! باید به خودمان پاسخ دهیم که آیا واقعاً نیاز است که سیستم را برای امکان استفاده از Ormهای مختلف طراحی کنیم؟
6- نحوه ماژول بندی سیستم و امکان افزودن راحت ماژولهای جدید به آن. امروزه و با افزایش کاربران محصولات انفورماتیک که باعث بیشتر شدن سواد مصرف کننده در این زمینه و بالطبع افزایش نیازهای وی شده، همیشه احتمال اینکه کارفرما موارد جدیدی را بخواهد وجود خواهد داشت. باید سیستم را طوری طراحی کرد که حتی بتوان بدون توقف اجرای آن موارد جدید (پلاگینهای جدید) را بدان افزود و اجرا کرد.
7- میزان مشارکت دیگران در رفع نیازمندیهای کابران. ممکن است این گزینه در درجه اول زیاد با اهمیت جلوه ندهد، اما با تعمق در وبسایتها و نرمافزارهای بزرگ که هم اکنون در دنیا صاحب نامی شدهاند میبینیم همه آنها تمهیداتی اندیشیدهاند تا با وجود کپسوله کردن موارد پس زمینه، امکاناتی را در جهت مشارکت دیگران فراهم کنند. اکثر شبکههای اجتماعی api هایی را مهیا کرده اند که افراد ثالث میتوانند از آنها استفاده کنند. اکثر سیستمهای مدیریت محتوا و ابزارهای e-commerce تمهیداتی را برای راحتی ساخت plugin و apiهای برای راحتی برقراری ارتباط اشخاص ثالث اندیشیدهاند. از نظر این جانب موارد 6 و 7 برای ادامه حیات و قابلیت رقابت پذیری پروژه از درجه اهمیت زیادی برخوردار است.
8- معماری Multi tenancy بلی یا خیر؟ Multi tenancy یک از بحثهای مهم رایانش ابری است. در این حالت فقط یک نمونه از نرم افزار در سمت سرور در حال اجراست ولی کاربر یا گروهی از کاربران دید یا تنظیمات متفاوتی از آنرا دارند.
در ادامه به موارد فنیتری خواهیم پرداخت:
9- بحث انتخاب ابزار Dependency injection مناسب و مهیا سازی امکاناتی جهت هرچه راحتتر کردن امکان تنظیم و register کردن اشیا بدان. نحوه پیکربندی مناسب این مورد میتواند کد نویسی را برایتان بسیار راحت کند. دات نت تیپس مطالب بسیاری را در این مورد ارائه داده است میتوانید اینجا را ببینید.
10- کشینگ. استفاده از یک سیستم کشینگ مناسب در ارتباط با بانکهای اطلاعاتی و یا سایر سیستمهای ذخیره و بازیابی اطلاعات میتواند کمک بسیاری در پرفرمنس برنامه داشته باشد. سیستمها و روشهای مختلفی در مورد کشینگ وجود دارند. میتوانید برای اطلاعات بیشتر اینجا را مطالعه فرمایید.
11- Logging. یک سیستم لاگر مناسب میتواند وارنینگها و خطاهای بوجود آمده در سیستم را در یک رسانه ذخیره سازی حفظ کند و شما به عنوان توسعه دهنده میتوانید با مطالعه آن نسبت به رفع خطاهای احتمالی و بهبود در نسخههای آتی کمک بگیرید.
12- Audit logging یا Activity logging و Entity History. میتوانید کل یا برخی از فعالیتهای کاربر را در یک رسانه ذخیره سازی، ذخیره کنید، از قبیل زمان ورود و خروج، آیپی مورد استفاده، سیستم عامل، مرورگر، بازبینی از صفحه وغیره. همچنین در audit logging میتوانید زمانهای دقیق تغییرات مختلف موجود در موجودیتهای سیستم، فرد انجام دهنده تغییرات، سرویس انجام دهنده تغییرات، مدت زمان سپری شده و ... را ذخیره کرد. Entity History : ممکن است تصمیم بگیرید که کل اتفاقاتی را که برای یک موجودیت در طول زمان حیاتش در سیستم میافتد، ذخیره کنید.
13- Eventing ، Background Workerها و Backgroudn jobs ( Scheduled tasks ). باید سیستم را طوری طراحی کرد که بتواند به تغییرات و اتفاقات افتاده در سیستم پاسخ دهد. همچنین این مورد یکی از نیازمندیهای معماری بر اساس پلاگین است. Background Workerها در واقع کارهایی هستند که در پس زمینه انجام میشوند و نیازی نیست که کاربر برای اتمام آن منتظر بماند؛ مثلاً ارسال ایمیل خوش آمدگویی را میتوان با آن انجام داد. Background jobs کمی متفاوت هستند در واقع اینها فعالیتهای پس زمینهای هستند که ممکن است در فواصل زمانی مختلف اتفاق بیافتند، مثل پاکسازی کش در فواصل زمانی مناسب. در سیستمهای مختلف تمهیداتی برای ذخیره سازی فعالیتهایی که توسط background jobs انجام میشود اندیشیده میشود.
14- پیکربندی صحیح نحوه ذخیره و بازیابی تنظیمات سیستم. در یک سیستم ممکن است شما تنظیمات متعددی را در اختیار کاربر و یا حتی خودتان قرار دهید. باید سیستم را طوری طراحی کنید که بتواند با راحتترین و سریعترین روش ممکن به تنظیمات موجود دستیابی داشته باشد.
15- خطاهای کاربر را در نظر بگیریم، باید یادمان باشد کاربر ممکن الخطاست و ما برای رضایت مشتری و قابلیت اتکای هرچه بیشتر برنامه باید سیستم را طوری طراحی کنیم که امکان برگشت از خطا برای کاربر وجود داشته باشد. مثلاً در SoftDelete مواردی که حذف میشوند در واقع به طور کامل از بانک اطلاعاتی حذف نمیشوند بلکه تیک حذف شده میخورند. پس امکان بازگردانی وجود خواهد داشت.
16- Mapping یا Object to object mapping. در توسعه شیءگرا مخصوصاً در معماریهایی مثل MVC یا Domain driven در موارد بسیاری نیاز خواهید داشت که مقادیر اشیاء مختلفی را در اشیای دیگری کپی کنید. سیستمهای زیادی برای این کار موجود هستند. باید تلاش کرد ضمن اینکه یک سیستم مناسب انتخاب کنیم، باید تمهیدی بیاندیشیم که تنظیمات آن شامل کد نویسی هرچه کمتری باشد.
17- Authorization یا تعیین هویت. باید با مطالعه و بررسی، سیستم و ابزار مناسبی را برای هویت سنجی اعضاء، تنظیم نقشها و دسترسیهای کاربران انتخاب کرد. باید امکان عضویت از طریق شبکههای اجتماعی مختلف را مورد بررسی قرار داد.
18- سرویسهای Realtime. کاربری یکی از مطالب شما را میپسندد و شما نوتیفیکشن آنرا سریع در صفحهای که باز کردید میبینید. این یک مورد بسیار کوچکی از استفاده از سرویسهای realtime هست. ابزارهای مختلفی برای زبانها و فریمورکهای مختلف وجود دارند؛ مثلاً میتوانید اینجا را مطالعه کنید.
19- هندل کردن خطاهای زمان اجرا، در سیستمهای قدیمی یکی از کابوسهای کاربران، قطعی سیستم، هنگ کردن با کوچکترین خطا و موارد این چنینی بود. با تنظیم یک سیستم Exception handling مناسب هم میتوانیم گزارشاتی از خطاهای بوجود آمده را تهیه کنیم، هم میتوانیم کاربر را در جهت انجام صحیح کارها هدایت کنیم و هم از کرش بیجای نرمافزار جلوگیری کنیم.
20- استفاده از منابع ابری یا توزیع شده، امروزه برای بسیاری از کارها تمهیداتی از طرف شرکتهای بزرگ به صورت رایگان و یا غیر رایگان اندیشیده شده است که به راحتی میتوان از آنها استفاده کرد. برای نمونه میتوان از سرویسهای Email به عنوان سادهترین و معمولترین این سیستمها یاد کرد. اما امروزه شرکتها حتی امکاناتی جهت ذخیره سازی دادههای blob (مجموعه ای از بایتها با حجم زیاد) را ارائه میدهند؛ امکانات دیگری نظیر کم کردن حجم تصاویر، تبدیل انواع mime typeها و ...
21- امنیت، فریمورکها اغلب موارد امنیتی پایهای را به صورت مطلوب یا نسبتا مطلوبی رعایت میکنند؛ ولی با اینحال باید در مورد امنیت سیستمی که توسعه میدهیم مطالعه داشته باشیم و موارد امنیتی ضروری را رعایت کنیم و همیشه مواظب باشیم که آنها را رعایت کنیم.
در این مقاله چه چیزی را پوشش خواهیم داد:
· راه اندازی داکر
· پیکرهبندی container image
· وصل شدن به sql
· ساخت یک پروژه ساده net core.
· ایجاد دیتابیس
· ثبت رکورد در دیتابیس
قبل از هرچیز باید داکر را بر روی سیستم عامل خود (لینوکس) نصب نماید. چون نصب داکر بر روی لینوکس از حوصلهی این مقاله خارج میباشد، میتوانید با مراجعه به این لینک docker را نصب کنید. پس از نصب docker، برای اطمینان حاصل نمودن از نصب، با دستور docker version میتوان کانفیگ داکر را مشاهده کرد:
دانلود و نصب sql server بر روی داکر
قبل از هرچیز باید Image اسکیوال سرور را بر روی داکر دانلود نمائید. برای این کار وارد سایت dockerhub شوید و عبارت microsoft/mssql-server-linux را جستجو کنید.
همانطور که در تصویر نیز مشاهد میکنید، این بسته 10 میلیون بار دریافت شدهاست! در ادامه دستور زیر را در ترمینال خود Paste کنید و منتظر بمانید تا دانلود شود:
docker pull microsoft/mssql-server-linux:2017-latest
برای اجرای image sql از دستور زیر استفاده میکنیم:
sudo docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=<YourStrong!Passw0rd>' \ -p 1433:1433 --name sql1 \ -d mcr.microsoft.com/mssql/server:2017-latest
Set the SA_PASSWORD : پسورد environment variable ای که شما انتخاب میکنید.
p 1433:1433- : شماره پورتی که Docker container بر روی آن اجرا میشود.
-d microsoft/mssql-server-linux:2017-latest : نام Image ای که میخواهیم اجرا کنیم.
همانطور که ملاحظه میکنید، در قسمت status، عبارت up به معنای در حال اجرا بودن container است. اگر عبارت دیگری را مشاهده کردید، با دستور dockr start id و وارد کردن شماره image خود میتوانید آن را اجرا کنید.
تا اینجا توانستیم sql server را اجرا کنیم. برای توضیحات بیشتر به این لینک مراجعه کنید.
وصل شدن به sql
برای وصل شدن به دیتابیس باید connection string دیتابیس مربوطه را داشته باشیم. با توجه به کانفیگهایی که در بالا انجام دادیم، connection string ما به شکل زیر خواهد بود:
Server Host: localhost Port: 1433 Authentication: SQL Server Authentication Login: SA Password: <StrongPasswordYouSet>
sudo docker exec -it sql1 "bash"
/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P '<YourNewStrong!Passw0rd>'
تا اینجای کار sql server آمادهی اجرا دستورات شما میباشد. در ادامه میخواهیم چند دستور سادهی sql را بر روی آن اجرا کنیم.
ساخت دیتابیس
با دستور sqlcmd زیر، ابتدا یک دیتابیس را میسازیم:
CREATE DATABASE TestDB
ساخت جدول
در ادامه، دستور زیر را برای ساخت جدول مینویسیم:
CREATE TABLE Inventory (id INT, name NVARCHAR(50), quantity INT)
ایجاد رکورد
مرحله بعدی، ایجاد یک رکورد جدید در دیتابیس میباشد:
INSERT INTO Inventory VALUES (1, 'banana', 150); INSERT INTO Inventory VALUES (2, 'orange', 154);
در آخر با استفاده از دستور go، کوئریهای بالا را اجرا میکنیم. اکنون باید یک دیتابیس جدید به نام TestDB و یک جدول جدید نیز به نام Inventory همچنین یک رکورد جدید در آن ثبت شده باشد. برای مشاهدهی تغییرات بالا، از دستورات زیر استفاده میکنیم:
- با دستور زیر لیست دیتابیسهای موجود را میتوان دید:
SELECT Name from sys.Databases
SELECT * FROM Inventory WHERE quantity > 152;
تا اینجا توانستیم docker را بر روی سیستم راه ندازی و همچنین sql server را بر روی آن نصب و اجرا کنیم. همچنین با دستورات sqlcmd توانستیم بر روی sql کوئری بزنیم.
ساخت و وصل شدن یک پروژهی net core. و وصل شدن به sql server
حال میخواهیم با یک پروژهی سادهی net core. به sql server فوق وصل شده و یک جدول را به دیتابیس مذکور اضافه کرده و یک کوئری اضافه کردن رکوردی را به آن جدول بنویسیم. برای شروع، یک پروژهی خالی net core. را ایجاد میکنیم. برای مثال یک پروژهی api را ایجاد میکنیم:
dotnet new webapi -o dockerapi
dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.EntityFrameworkCore.Design
public class Students { public int Id { get; set; } public string Name { get; set; } public string Phone { get; set; } }
dotnet ef dbcontext scaffold "Server=localhost,1433\\Catalog=tutorial_database;Database=<YOUR_DATABASE_NAME>;User=SA;Password=<StrongPasswordYouSet>;" Microsoft.EntityFrameworkCore.SqlServer
"ConnectionStrings": { "TestingDatabase": "Server=localhost:1433\\Database=<YourDatabaseName>;User=SA;Password=<StrongPasswordYouSet>;" }
dotnet ef migrations add <NAME_OF_MIGRATION>
همانطور که مشاهده میکنید، migrations اضافه شده و موجودیت هم اضافه شدهاست. حال باید بر روی migrations خود آپدیت بزنیم:
ef database update
SELECT TABLE_NAME FROM dockerdb.INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'
کتابخانه NetTopologySuite
بررسی نحوهی راه اندازی پروژهی Decision
امن سازی برنامههای وب همواره چالش برانگیز بودهاست؛ خصوصا این روزها که نیاز است برنامهها، خارج از دیوارهای یک شرکت نیز در دسترس باشند و توسط انواع و اقسام وسایل ارتباطی مورد استفاده قرار گیرند. در سالهای قبل، عموما برنامههای thick clients مانند WPF و WinForms برای شرکتها توسعه داده میشدند و یا برنامههای وب مانند ASP.NET Web Forms که مبتنی بر سرویسها نبودند. در برنامههای ویندوزی، پس از لاگین شخص به شبکه و دومین داخلی شرکت، عموما از روش Windows Authentication برای مشخص سازی سطوح دسترسی کاربران استفاده میشود. در برنامههای Web Forms نیز بیشتر روش Forms Authentication برای اعتبارسنجی کاربران مرسوم است. امن سازی این نوع برنامهها سادهاست. عموما بر روی یک دومین ارائه میشوند و کوکیهای اعتبارسنجی کاربران برای ارائهی مباحثی مانند single sign-on (داشتن تنها یک صفحهی لاگین برای دسترسی به تمام برنامههای شرکت)، میسر است.
عصر شروع بهکارگیری سرویسهای وب
در سالهای اخیر این شیوهی کاری تغییر کرده و بیشتر بر اساس بکارگیری برنامههای مبتنی بر سرویسها شدهاست. برای مثال برای این مورد استاندارد WS-Security مربوط به WCF ارائه شدهاست که باز هم مرتبط است به برنامههای یک دومین و یا یک Application pool. اگر تعداد دومینها بیشتر شوند و نیاز به ارتباط امن بین آنها باشد، استاندارد SAML 2.0 مورد استفاده قرار میگرفت که هدف از آن، انتقال امن اعتبارسنجی و سطوح دسترسی کاربران بین دومینهای مختلف است. همانطور که ملاحظه میکنید تمام این برنامهها و استانداردها، داخل دیوارهای یک شرکت و یک دومین زندگی میکنند.
عصر شروع بهکارگیری Restful-API's
پس از آن باز هم شیوهی طراحی برنامههای تغییر کرد و شروع به ایجاد Restful-API's و HTTP API's کردیم. اینها دیگر الزاما داخل یک دومین ارائه نمیشوند و گاهی از اوقات حتی تحت کنترل ما هم نیستند. همچنین برنامههای ارائه شده نیز دیگر thick clients نیستند و ممکن است برنامههای سمت کلاینت Angular و یا حتی موبایل باشند که مستقیما با سرویسهای API برنامهها کار میکنند. حتی ممکن است یک API را طراحی کنیم که با یک API دیگر کار میکند.
در این حالت دیگر نمیتوان این APIها را با نگهداری آنها داخل دیوارهای یک شرکت محافظت کرد. اگر قرار است با یک HTTP API کار کنیم، این API باید عمومی باشد و در اینجا دیگر نمیتوان از روش Forms Authentication استفاده کرد. زیرا این روش اعتبارسنجی مختص برنامههای سمت سرور قرار گرفتهی در یک دومین طراحی شدهاست و آنچنان سازگاری با برنامههای سمت کلاینت و موبایل خارج از دیوارهای آن ندارد. همچنین ارسال نام کاربری و کلمهی عبور به ازای هر درخواست نیز روشی بسیار بدوی و نا امن است. اینجا است که عصر امن سازی برنامهها به کمک توکنها شروع میشود. با استفادهی از توکنها، بجای هر بار ارسال نام کاربری و کلمهی عبور به ازای هر درخواست از API، صرفا لیست سطوح دسترسی امضا شدهی به امکاناتی خاص، ارسال میشوند.
عصر شروع بهکارگیری Security Tokens
بنابراین در اینجا نیاز به برنامهای برای تولید توکنها و ارسال آنها به کلاینتها داریم. روش متداول پیاده سازی آن، ساخت یک برنامهی ابتدایی، برای دریافت نام کاربری و کلمهی عبور از کاربران و سپس بازگشت یک JSON Web Token به آنها است که بیانگر سطوح دسترسی آنها به قسمتهای مختلف برنامه است و کاربران باید این توکن را به ازای هر درخواستی به سمت سرور (بجای نام کاربری و کلمهی عبور و خود) ارسال کنند.
مشکل این روش در اینجا است که آن برنامهی خاص، باید از نام کاربری و کلمهی عبور کاربران مطلع باشد تا بتواند توکن مناسبی را برای آن کاربر خاص تولید کند. هر چند این روش برای یک تک برنامهی خاص بسیار مناسب به نظر میرسد، اما در یک شرکت، دهها برنامه مشغول به کارند و به اشتراک گذاری نام کاربری و کلمهی عبور کاربران، با تک تک آنها ایدهی مناسبی نیست و پس از مدتی از کنترل خارج خواهد شد. برای مثال کاربری در یک برنامه، کلمهی عبور خود را تغییر میدهد اما در برنامهای دیگر خیر و همین مسالهی عدم هماهنگی بین برنامهها و همچنین بخشهای مختلف یک شرکت، مدیریت یک دست برنامهها را تقریبا غیر ممکن میکند. همچنین در اینجا برنامههای ثالث را نیز باید در نظر داشت که آیا ضروری است آنها به ریز اطلاعات کاربران شرکت دسترسی پیدا کنند؟
به علاوه مشکل دیگر توسعهی این نوع برنامههای صدور توکن خانگی، اختراع مجدد چرخ است. در اینجا برای بهبود امنیت برنامه باید منقضی شدن، تمدید، امضای دیجیتال و اعتبارسنجی توکنها را خودمان پیاده سازی کنیم. توسعهی یک چنین سیستمی اگر غیرممکن نباشد، بسیار سخت و پیچیده است و همچنین باید باگهای امنیتی ممکن را نیز مدنظر داشت.
بنابراین تا اینجا به این نتیجه رسیدهایم که دیگر نمیخواهیم مدیریت نام کاربری و کلمهی عبور کاربران را در سطح هیچکدام از برنامههای خود انجام دهیم و هیچکدام از آنها قرار نیست دریافت کنندهی این اطلاعات باشند. قرار است این کار، به یک تک برنامهی مرکزی مخصوص اینکار منتقل شود و برای اینکار نیاز به پروتکلی امن است که بتوان این توکنهای تولیدی را ارسال و پردازش کرد.
حرکت به سمت «تامین کنندهی هویت مرکزی»
در گذشته، هر تک برنامهای دارای صفحهی لاگین و امکانات مدیریت کاربران آن، تغییر کلمهی عبور، تنظیم مجدد آن و اینگونه عملیات بود. اینروزها دیگر چنین کاری مرسوم نیست. این وظیفهی برنامهی شما نیست که بررسی کند کاربر وارد شدهی به سیستم کیست و آیا ادعای او صحیح است یا خیر؟ این نوع عملیات وظیفهی یک Identity provider و یا به اختصار IDP است. کار IDP اعتبارسنجی کاربر و در صورت نیاز، ارائهی اثبات هویت کاربر، به برنامهی درخواست کنندهاست.
در یک IDP عملیاتی مانند ثبت کاربران و مدیریت آنها انجام میشود. اینجا است که مفاهیمی مانند قفل کردن اکانت و یا تغییر کلمهی عبور و امثال آن انجام میشود و نه اینکه به ازای هر برنامهی تهیه شدهی برای یک شرکت، آن برنامه راسا اقدام به انجام چنین عملیاتی کند. به این ترتیب میتوان به امکان استفادهی مجدد از اطلاعات هویت کاربران و سطوح دسترسی آنها در بین تمام برنامههای موجود رسید.
همچنین با داشتن یک برنامهی IDP خوب پیاده سازی شده، از توزیع باگهای امنیتی مختلف در بین برنامههای گوناگون تهیه شده که هر کدام سیستم امنیتی خاص خودشان را دارند، جلوگیری خواهد شد. برای مثال فرض کنید میخواهید الگوریتم هش کردن پسوردهای سیستم را که امروز نا امن اعلام شدهاست، تغییر دهید. با داشتن یک IDP، دیگر نیازی نیست تا تمام برنامههای خود را برای رفع یک چنین باگهایی، تک تک تغییر دهید.
به علاوه این روزها روش استفادهی از نام کاربری و کلمهی عبور، تنها راه ورود به یک سیستم نیست و استفاده از کلیدهای دیجیتال و یا روشهای ویژهی ابزارهای موبایل نیز به این لیست اضافه شدهاند.
حرکت به سمت استاندارد OAuth 2
OAuth 2.0 پروتکلی است استاندارد، برای Authorization امن کاربران، توسط برنامههای وب، موبایل و دسکتاپ. به این ترتیب میتوان امکان دسترسی یک برنامه را به یک API، به نحوی استاندارد و امن میسر ساخت. OAuth 2.0 یک توکن دسترسی (Access token) را تولید میکند و در اختیار کلاینت قرار میدهد. سپس آن کلاینت با ارسال این توکن به API مدنظر، امکان دسترسی به امکانات مختلف آنرا خواهد یافت. به علاوه چون ماهیت برنامههای کلاینت وب و غیر وب متفاوت است، این استاندارد نحوهی دریافت چنین توکنی را برای برنامههای مختلف نیز تعریف میکند. به این ترتیب موارد مشترکی مانند تولید و نحوهی انتقال توکنها به کلاینتهای مختلف توسط این پروتکل استاندارد بیان میشود. در این حالت راهحلهای خانگی ما تبدیل به راهحلهایی میشوند که استاندارد OAuth 2.0 را پیاده سازی کرده باشند. بنابراین IDP ما باید بر مبنای این استاندارد تهیه شده باشد. برای مثال IdentityServer که در این سری بررسی خواهد شد و یا Azure Active Directory، نمونهای از IDPهایی هستند که این استاندارد را کاملا پیاده سازی کردهاند.
البته باید دقت داشت که این توکنهای دسترسی، تنها سطوح دسترسی به منابع API را مشخص میکنند و یا به عبارتی عملیات Authorization توسط آنها میسر میشود. عملیات ورود به سیستم اصطلاحا Authentication نام دارد و توسط استاندارد دیگری به نام OpenID Connect مدیریت میشود.
حرکت به سمت استاندارد OpenID Connect
OpenID Connect یک لایهی امنیتی بر فراز پروتکل OAuth 2.0 است که به اختصار به آن OIDC نیز گفته میشود. توسط آن یک کلاینت میتواند یک Identity token را علاوه بر Access token درخواست کند. از این Identity token برای ورود به برنامهی کلاینت استفاده میشود (Authentication) و پس از آن، برنامهی کلاینت بر اساس سطوح دسترسی تعریف شدهی در Access token، امکان دسترسی به امکانات مختلف یک API را خواهد یافت (Authorization). همچنین OpenID Connect امکان دسترسی به اطلاعات بیشتری از یک کاربر را نیز میسر میکند.
بنابراین OpenID Connect پروتکلی است که در عمل استفاده میشود و توسعه دهنده و جایگزین کنندهی پروتکل OAuth 2.0 میباشد. هرچند ممکن است در بسیاری از منابع صرفا به OAuth 2.0 بپردازند، اما در پشت صحنه با همان OpenID Connect کار میکنند.
مزیت دیگر کار با OpenID Connect، عدم الزام به استفادهی از API، در برنامهای خاص و یا قدیمی است. اگر برنامهی وب شما با هیچ نوع API ایی کار نمیکند، باز هم میتوانید از امکانات OpenID Connect بهرهمند شوید.
- اگر از JWE استفاده نمیکنید، بهتر است اطلاعات حساسی مانند شماره تلفن کاربر (و شاید در مواردی حتی آیدی کاربر) را در بدنه توکن قرار ندهیم چرا که قابل خوانده شدن است (که در این صورت استفاده از Guid برای آیدی کاربر می تواند کمی مفید باشد چرا که حداقل آیدی بقیه کاربران قابل پیش بینی نمیباشد).
- توکن JWT هیچ امنیتی در برابر خوانده شدن ندارد؛ ولی به لطف امضای (signature) آن، در برابر تغییر محتوا، ایمن است؛ چرا که در صورت تغییر محتوای آن، دیگر مقدار hash محتوا با امضای آن همخوانی نداشته و عملا از اعتبار ساقط میگردد.
var secretKey = Encoding.UTF8.GetBytes("LongerThan-16Char-SecretKey"); // must be 16 character or longer var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha256Signature); var encryptionkey = Encoding.UTF8.GetBytes("16CharEncryptKey"); //must be 16 character var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encryptionkey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256); var claims = new List<Claim> { new Claim(ClaimTypes.Name, "UserName"), //user.UserName new Claim(ClaimTypes.NameIdentifier, "123"), //user.Id }; var descriptor = new SecurityTokenDescriptor { Issuer = _siteSetting.JwtSettings.Issuer, Audience = _siteSetting.JwtSettings.Audience, IssuedAt = DateTime.Now, NotBefore = DateTime.Now.AddMinutes(_siteSetting.JwtSettings.NotBeforeMinutes), Expires = DateTime.Now.AddMinutes(_siteSetting.JwtSettings.ExpirationMinutes), SigningCredentials = signingCredentials, EncryptingCredentials = encryptingCredentials, Subject = new ClaimsIdentity(claims) }; var tokenHandler = new JwtSecurityTokenHandler(); var securityToken = tokenHandler.CreateToken(descriptor); string encryptedJwt = tokenHandler.WriteToken(securityToken);
در ادامه لازم است در مرحله اعتبار سنجی و رمزگشایی توکن در سمت سرور، کلید و الگوریتم لازم را به آن معرفی کنیم تا middleware مربوطه بتواند توکن دریافتی را رمزگشایی و سپس اعتبار سنجی کند. بدین منظور در متد ConfigureServices کلاس Startup.cs خواهیم داشت:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { var secretkey = Encoding.UTF8.GetBytes("LongerThan-16Char-SecretKey"); var encryptionkey = Encoding.UTF8.GetBytes("16CharEncryptKey"); var validationParameters = new TokenValidationParameters { ClockSkew = TimeSpan.Zero, // default: 5 min RequireSignedTokens = true, ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(secretkey), RequireExpirationTime = true, ValidateLifetime = true, ValidateAudience = true, //default : false ValidAudience = "MyWebsite", ValidateIssuer = true, //default : false ValidIssuer = "MyWebsite", TokenDecryptionKey = new SymmetricSecurityKey(encryptionkey) }; options.RequireHttpsMetadata = false; options.SaveToken = true; options.TokenValidationParameters = validationParameters; });
کد بالا مانند کد فعال سازی احراز هویت توسط JWT معمولی در ASP.NET Core است؛ با این تفاوت که:
ابتدا آرایه بایتی همان کلید رمزنگاری (encryptionkey) که قبلا توکن را با آن رمزنگاری کرده بودیم، گرفته شده و سپس توسط مقداردهی خاصیت TokenDecryptionKey کلاس TokenValidationParameters، معرفی شده است.
ولی شاید این سؤال برایتان پیش آید که چرا الگوریتم رمزنگاری مشخص نشده است؟ پس سرور از کجا میفهمد که این توکن بر اساس چه الگوریتمی رمزنگاری شده است؟
دلیل آن این است که به هنگام تولید توکن، اسم الگوریتم مربوطه، داخل بخش header توکن نوشته میشود. اگر تصویر قبل را مشاهده کنید مقدار header توکن به شرح زیر است.
{ "alg": "A128KW", "enc": "A128CBC-HS256", "typ": "JWT" }
پس سرور بر اساس این قسمت از توکن (header)، که هیچگاه رمزنگاری نمیشود، میفهمد که توسط چه الگوریتمی باید توکن را رمزگشایی کند که در اینجا A128CBC-HS256 (اختصار AES-128-CBC و HMAC-SHA256) است.
مثال کامل و قابل اجرای این مطلب را میتوانید از این ریپازیتوری دریافت کنید.
The first preview of Entity Framework Core (EF Core) 8 is available on NuGet today!
Basic information
EF Core 8, or just EF8, is the successor to EF Core 7, and is scheduled for release in November 2023, at the same time as .NET 8.
EF8 currently targets .NET 6. This will likely be updated to .NET 8 as we near release.
EF8 will align with .NET 8 as a long-term support (LTS) release. See the .NET support policy for more information.