السلام عليكم ورحمة الله وبركاته
تقريبا في الأسبوعين السابقين كان في أسئلة من قبل الأعضاء حول موضوع المستخدمين وتخزين كلمات مرورهم وتحديد كم طول كلمة المرور وكم الحد الأدنى للأرقام في كلمة المرور والحد الأدنى للحروف الخاصة وما إلى ذلك. أولا خلونا نتفق على بعض المسطلحات عشان نكون كلنا في إتجاه واحد. عملية تخزين المستخدمين بكلمات مرورهم في الـ ASP.NET تسمى بالـ Membership، بينما عملية تسجيل دخول المستخدم والتأكد من كلمة المرور تسمى بالـ Authentication. أما عملية تقسيم المستخديمن إلى مجموعات (مثلا المشرفين، المدراء، المراقبين .... إلخ) تسمى بالـ Roles، عملية السماح أو عدم السماح لمستخدم معين بالوصول إلى مصدر معين بناء على الـ Roles الذي ينتمي إليه أو إليهم تسمى بالـ Authorization. الشخص الذي لا يمتلك اسم مستخدم يسمى بالـ Anonymous User.
أولا : ما هي أنواع الـ Authentication في الـ ASP.NET :
عندما تقوم بإنشاء مشروع جديد عن طريق VS 2005/2008 وتقوم بتحديد المشروع على أنه ASP.NET website لاحظ أنه سيتم إضافة ملف خاص بإسم web.config، بداخله ستلاحظ العبارة أو الإدخال التالي :
<system.wbe> . . . <authentication mode="Windows" /> . . . </system.web>
هذا هو الإدخال الذي من خلالة تقوم بتحديد طريقة أو نوع الـ Authentication لمشروعك وكما نرى في حالتنا هذه أنه بشكل إفتراضي يكون Windows. والمقصود هنا اسم المستخدم الذي دخلت به في نظام MS Windows (أي أن عملية تسجيل الدخول والتحقق من كلمة المرور ستكون عن طريق نظام تشغيل Windows الموجود في الجهاز الحالي الذي عن طريقة ستقوم بتصفح هذه الصفحة أو الموقع). فلو قمنا بتغيير هذا النوع سنلاحظ أن VS 2005/2008 سيقوم بإقتراح Windows, Forms, Passport, None. حيث أن Windows سبق شرحه أعلاه، و Passport لم أستخدمها قط ولكن مما قرأت يقال أن هناك ما يسمى بالـ Microsoft's Passport Service وهي ستكون مسؤولة عن التحقق من اسم المستخدم وكلمة المرور، أما None فهي كلمة معبرة عن نفسها ولاتحتاج إلى شرح، وأخيرا وليس آخرا Forms وهذا ما سوف نتطرق إليه بعد قليل.
ملاحظة : عملية التحقق من إسم المستخدم وكلمة المرور تسمى بالـ Authentication Formalities.
Forms Authentication :
من منا لم يستخدم الـ Forms Authentication ، عندما تقوم بتسجيل الدخول في هذا المنتدى أو في تسجيل الدخول لبريدك الإلكتروني أو لحسابك البتكي والأمثلة كثيرة. بكل بساطة الـ Forms Authentication عبارة عن القيام بـ Authentication Formalities عن طريق HTML Form بحيث (في أبسط صورة) يوجد هناك حقلين أحدهما لإسم المستخدم والثاني لكلمة المرور.
ثانيا : تطبيق Forms Authentication على مشروعك
يمكنك فعل ذلك بعدة ضغطات بالفأرة عن طريق إستخدام ASP.NET Website Administration Tool (AWAT), plain & simple. هنا لن نتطرق لهذه الطريقة ولكن سنقوم بتحديد ما يترتب على ذلك فقط. عندما تقوم بإستخدام AWAT ستلاحظ أن هناك خطوات أو Wizard من خلالها ستتمكن من تجهيز موقعك، وتشمل هذه الخطوات تحديد نوع الـ Authentication وكم طول كلمة المرور، وهل البريد الإلكتروني يتكرر أم لا، وهل فرض إستخدام السؤال السري وما هو وما هي الإجابات المحتملة و و و و. بعد أن تنتهي من تحديد ما سبق وبعد أن تقوم بإدخال بعض المستخدمين والـ Roles عن طريق AWAT ستلاحظ أن هناك مجلد App_Data تم إضافة في مشروعك وبداخله ملف aspnetdb.mdf، قم بفتح الملف ستلاحظ أن AWAT قد قام ببناء قاعدة بينات كاملة ومتكاملة وصدقني أنه يمكنك الإعتماد عليها جدا حيث أنه ستلاحظ أن كلمات المرور وبعض البيانات المهمة قد تم تشفيرها داخل جداول قاعدة البيانات aspnetdb.mdf. بعد ذلك قم بإستخدام الأدوات الجاهزة والخاصة بالـ Authentication مثل LoginControl ومل إلى ذلك.
سؤال : ماذا لو كانت عندك قاعدة بينات ولنفرض أنها MS SQL - حيث لا تكون هناك مشكلة في التوافق - وأنت تريد إضافة الجداول الموجودة في aspnetdb.mdf إلى قاعدة بياناتك بدل من أن يتم إضافتها في ملف خارجي ؟
طبعا قد يسأل شخص وما الضرر في أن تكون الجداول المتعلقة بالـ Authentication مفصولة، ستكون الإجابة هي كالتالي : في كل مرة مشروعك يحتاج إلى أداء أي شيء متعلق بالـ Authentication سيقوم بالإتصال بملف الـ aspnetdb.mdf، وفي كل مرة يحتاج أن يقوم بأي شيء يتعلق بالـ business الذي يقوم به سوف يتصل بقاعدة البيانات التي بها الجداول المتعلقة بالـ business الذي بسببة تم إنشاء هذا المشروع، فهناك قاعدتي بيانات فقد يرغب الشخص بتغيير السؤال إلى : لماذا لا يكون هناك مركزية لقاعدة البيانات. عموما بناء الجداول المتعلقة بالـ Authentication داخل قاعدة بينات موجودة مسبقا بسيط جدا كل ما تحتاج إليه هو فقط إستخدام ASP.NET SQL Server Registration Tool عن طريق الذهاب إلى المجلد الذي تم تثبيت الـ dot Net Framework وكتابة الأمر aspnet_regsql بإستخدام الـ command-line كما هو موضح أدناه
%WINDOWS%\Microsoft.NET\Framework\<your .net framework version>\aspnet_regsql
بعد ذلك سيظهر لك wizard قم بإتباعها إلى النهاية وسيتم بناء الجداول داخل قاعدة البيانات التي تم تحديدها بإستخدام الـ wizard.
سؤال : ماذا لو كان هناك جداول خاصة بك أو بمشروعك تحوي على معلومات الـ Authentication وتريد إستخدامها بدل من الجداول التي سيتم بنائها عن طريق AWAT أو aspnet_regsql، ماذا لو كانت الجداول الناتجة عن كل من AWAT و aspnet_regsql أكثر من اللازم لمشروعك أو أنك فقط تريد تطبيق نظام Authentication بسيط جدا ؟ ماذا لو كانت قاعدة البيانات الموجودة Oracle أو MySql أو شيء غير MSSQL، بمعنى آخر ماذا لو ان الأداة aspnet_regsql لا تفيدك ؟
ثالثا : A simple Authentication System
هنا سنقوم ببناء نظام الـ Authentication الخاص بناء وسوف نحاول أن نجعله بسيط جدا، سنقوم ببناء بعض الجداول المتعلقة بموضوعنا هذا إبتداء بجدول المستخدمين وإنتهاء بالجدول الخاص بالـ Roles، فكل خطوة سنقوم بها سأقوم بكتابة تركيب الجدول الذي نحتاجة.
المستخدمين users_tbl: سنقوم ببناء جدول المستخدمين (users_tbl) كما هو موضح أدناه :
create table users_tbl ( uid int primary key identity(1,1), uname varchar(50), password varchar(10), email varchar(50) )
إضافة الإعدادات اللازمة في ملف web.config :
المقصود هنا تحديد نوع الـ Authentication وعنوان الصفحة التي سيتم توجيه الـ anonymous user إليها في حالة تطلب تسجيل الدخول للوصول لمصدر معين (سنرى هذه العملية لاحقا) وأيضا الصفحة الإفتراضية التي سيتم توجيه المستخدم إليها بعد أن تتم عملية الـ Authentication Formalities، بشكل إفتراضي ستكون صفحة تسجيل الدخول هي login.aspx ولكن يمكنك تغيير ذلك، قم بإضافة التالي في ملف web.config :
<system.web>
.
.
.
<authentication mode="Forms">
<forms loginUrl="~/login.aspx" defaultUrl="~/Default.aspx" />
</authentication>
.
.
.
</system.web>بناء class يساعدنا في عملية Authentication Formatlities وتسجيل المستخدمين "Utilities.cs" :
قم بإضافة مجلد خاص عن طريق الضغط بالزر اليمين على الـمشروع ومن ثم Add ASP.NET Folder ومن ثم App_Code وبعد ذلك قم بإضافة class بإسم Utilities.cs داخل App_Code وبعد ذلك قم بكتابة التالي :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.SqlClient;
/// <summary>
/// Summary description for Utilities
/// </summary>
public static class Utilities
{
// Make sure to change the connectionString to your relating MDF file
private const string connectionString =
@"data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|mydb.mdf;User Instance=true";
// Note: Exceptions are not handled in any of the following codes, try to handle them the way
// you see fit
public static void RegisterNewUser(string username, string password, string email)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(@"insert into users_tbl(uname, password, email)
values(@uname, @password, @email)", con);
// Note: do not use OledbParameters.AddWithValue with complex data type (image, blob, date, binary ... etc)
cmd.Parameters.AddWithValue("uname", username);
cmd.Parameters.AddWithValue("password", password);
cmd.Parameters.AddWithValue("email", email);
con.Open();
cmd.ExecuteNonQuery();
}
}
}بناء صفحة لتسجيل المستخدمين "register.aspx" :
أعتقد أن اسم الصفحة يصف مهمتها، بم بإضافة صفحة بإسم register.aspx بالشكل التالي :
<h3 style="text-align: center">
Register New Account</h3>
<table border="0" cellpadding="5px" cellspacing="0" style="width: 350px; border: 1px dashed #07f;
border-collapse: collapse; margin: 0 auto;">
<tr>
<td>
User Name
</td>
<td>
<asp:TextBox ID="usernameTextBox" MaxLength="50" runat="server" />
<asp:RequiredFieldValidator ID="usernameRequiredFieldValidator" runat="server" Text="*"
ErrorMessage="You must provide a username!" ControlToValidate="usernameTextBox" />
</td>
</tr>
<tr>
<td>
Password
</td>
<td>
<asp:TextBox ID="passwordTextBox" MaxLength="10" runat="server" TextMode="Password" />
<asp:RequiredFieldValidator ID="passwordRequiredFieldValidator" runat="server" Text="*"
ErrorMessage="You must provide a password!" ControlToValidate="passwordTextBox" />
</td>
</tr>
<tr>
<td>
Retype Password
</td>
<td>
<asp:TextBox ID="retypePasswordTextBox" MaxLength="10" runat="server" TextMode="Password" />
<asp:CompareValidator ID="passwordCompareValidator" runat="server" Text="*" ErrorMessage="Password doesn't mathc!"
ControlToValidate="retypePasswordTextBox" ControlToCompare="passwordTextBox"
Operator="Equal" />
</td>
</tr>
<tr>
<td>
E-Mail
</td>
<td>
<asp:TextBox ID="emailTextBox" MaxLength="50" runat="server" />
<asp:RegularExpressionValidator ID="emailRegularExpressionValidator" runat="server"
ControlToValidate="emailTextBox" Text="*" ErrorMessage="Your email is not in a correct form!"
ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" />
</td>
</tr>
<tr>
<td>
</td>
<td style="text-align: right;">
<asp:Button ID="saveButton" runat="server" Text="Register" OnClick="saveButton_Click" />
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:Label ID="statusLabel" runat="server" Text="" ForeColor="Green" />
</td>
</tr>
<tr>
<td colspan="2">
<asp:ValidationSummary ID="registeringValidationSummary" runat="server" />
</td>
</tr>
</table>
/****************************************
saveButton_Click handler
****************************************/
protected void saveButton_Click(object source, EventArgs e)
{
Utilities.RegisterNewUser(usernameTextBox.Text, passwordTextBox.Text, emailTextBox.Text);
statusLabel.Text = string.Format("Thank you '{0}' for your registration.", usernameTextBox.Text);
}إضافة صفحة تسجيل الدخول "login.aspx" :
هذه الصفحة لا تحتاج إلى شرح، ولكن قبل أن نضيفها أود أن أخبركم بأنه لن نستخدم الـ Login Control الموجود في الأدوات بل سنستخدم فقط TextBox للقيام بعملية الـ Authentication Formalities، ولكن قبل ذلك يجب أن نضيف AuthenticateUser Method في الـ Utilities Clss والغرض منها هو القيام بعملية الـ Authentication Formalities، كما هو موضح في التالي :
public static bool AuthenticateUser(string username, string password)
{
bool isAuthenticated = false;
using (SqlConnection con = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(
"select count(uname) from users_tbl where uname=@uname and password=@password",
con);
cmd.Parameters.AddWithValue("uname", username);
cmd.Parameters.AddWithValue("password", password);
con.Open();
isAuthenticated = int.Parse(cmd.ExecuteScalar().ToString()) == 1;
}
return isAuthenticated;
}الآن سنقوم بإنشاء صفحة الـ login.aspx وسوف نرى كيفية إستخدام System.Web.Security.FormsAuthentication.RedirectFromLoginPage حيث أنها ستقوم بتسجيل cookie في متصفح المستخدم يدل على أن هذا المستخدم تم المصادقة عليه وبعد ذلك سيتم إعادة توجيهه إما إلى الصفحة التي تطلب فيها تسجيل الدخول (أي كانت خاصة بحيث لا يدخلها من ليس له حساب أو من يجب أن ينتمي إلى مجموعة أو Role معين، سوف نراى ذلك لاحقا)، أو إلى الصفحة الإفتراضية التي تم تحديدها في ملف web.config في العنصر authentication، كما هو موضح في الكود التالي :
<h3 style="text-align: center">
Login</h3>
<table border="0" cellpadding="5px" cellspacing="0" style="width: 350px; border: 1px dashed #07f;
border-collapse: collapse; margin: 0 auto;">
<tr>
<td>
Username
</td>
<td>
<asp:TextBox ID="usernameTextBox" MaxLength="50" runat="server" />
<asp:RequiredFieldValidator ID="usnernameRequiredFieldValidator" runat="server" Text="*"
ErrorMessage="Please provide a username!" ControlToValidate="usernameTextBox" />
</td>
</tr>
<tr>
<td>
Password
</td>
<td>
<asp:TextBox ID="passwordTextBox" MaxLength="10" runat="server" TextMode="Password" />
<asp:RequiredFieldValidator ID="passwordRequiredFieldValidator" runat="server" Text="*"
ErrorMessage="Please provide a password!" ControlToValidate="passwordTextBox" />
</td>
</tr>
<tr>
<td>
</td>
<td style="text-align: right;">
<asp:Button ID="loginButton" runat="server" Text="Login" OnClick="loginButton_Click" />
</td>
</tr>
<tr>
<td>
</td>
<td style="text-align: right;">
<a href="register.aspx">Not registered? What are you waiting for ?</a>
</td>
</tr>
<tr>
<td>
</td>
<td>
<asp:Label ID="errorLoginLabel" runat="server" runat="server" ForeColor="Red"
Text="No such user is found or invalid password" Visible="false" />
</td>
</tr>
<tr>
<td colspan="2">
<asp:ValidationSummary ID="loginValidationSummary" runat="server" />
</td>
</tr>
</table>
/****************************************
loginButton_Click handler
****************************************/
protected void loginButton_Click(object source, EventArgs e)
{
if (Utilities.AuthenticateUser(usernameTextBox.Text, passwordTextBox.Text))
FormsAuthentication.RedirectFromLoginPage(usernameTextBox.Text, false);
else errorLoginLabel.Visible = true;
}طبعا لو تلاحظوا الـ System.Web.Security.FormsAuthentication.RedirectFromLoginPage تاخذ two arguments الأولى عبارة عن إسم المستخدم الي راح يصدر له Cookie اما الثانية فهي قيمة بولينية لو true معناته إن الـ cookie راح تكون دائمة أو ثابته "Persistence"، لو كان false فمعناته راح تنتهي "expire" الـ cookie بمجرد ما يقوم المستخدم بإغلاق المتصفح، لاحظوا إننا أرسلنا الـ argument الثاني بقيمة false ويعود السبب إلى إمكانية حفظ الـ cookie لمدة 50 سنة (can you believe that crap!!!!) وهذا قد يولد ثغرة إمكانية التنصت أو أخذ الـ cookie وبالتالي راح نخسر الـ privacy factor.
إضافة LoginName Control في الصفحة "Default.aspx" :
سوف نقوم بإضافة LoginName Control في الصفحة المذكورة أعلاة فقط لنرى هل سوف تقوم الـ ASP dotNet فعلا بتسجيل cookie وبالتالي ظهور اسم المستخدم أم لا، ,وأيضا سوف نستخدم الـ LoginStatusControl وهو يقوم بعرض رابط إما تسجيل الدخول أو تسجيل الخروج على حسب حالة المستخدم الذي يتصفح الصفحة ، قم بكتابة التالي :
<h3 style="text-align: center;">
Default.aspx
</h3>
<p>
<asp:LoginName ID="loginName" runat="server" FormatString="Welcome '{0}'" />
</p>
<asp:LoginStatus ID="loginStatus" runat="server" LoginText="Click here to login"
LogoutText="Click here to logout" />
<br />
<a href="register.aspx">Not registered? What are you waiting for ?</a>لاحظوا أن الـ LoginStatus سيقوم بقراءة الإعدادات من ملف web.config والمقصود بها صفحة تسجيل الدخول والصفحة الإفتراضية التي سيعاد توجيه المستخدم إليها بعد تسجيل الخروج
بعد كل ما تم شرحه بقي علينا أن نرى كيفية إضافة Roles وكيفية تحديد مستوى الوصل إلى بعض موارد الموقع إما على حسب حالة المستخدم (Authenticated / Anonymous) أو على حسب أي Role ينتمي إليه المستخدم. بإذن الله سنكمل في الدرس القادم.
أتمنى أن أكون قد أصبت في قولي وأن يكون شرحي واضح وموجزوأيضا أتمنى لكم التوفيق.
مرفق المشروع الذي أستند عليه في شرحي هذا
ألهم أرزقنا جميعا علما نافع غير ضار ولا تحرمنا جنانك ورؤية الأبرار، الهم آمين



