در ادامه بررسی تصویر امنیتی سایت مواردی که باید پیاده سازی شود برای مورد اول میتوان کلاس زیر را
در نظر گرفت که متدهایی برای تولید اعداد بصورت تصادفی در بین بازه معرفی شده را بازگشت میدهد:
// RandomGenerator.cs
using System;
using System.Security.Cryptography;
namespace PersianCaptchaHandler
{
public class RandomGenerator
{
private static readonly byte[] Randb = new byte[4];
private static readonly RNGCryptoServiceProvider Rand = new RNGCryptoServiceProvider();
public static int Next()
{
Rand.GetBytes(Randb);
var value = BitConverter.ToInt32(Randb, 0);
if (value < 0) value = -value;
return value;
}
public static int Next(int max)
{
Rand.GetBytes(Randb);
var value = BitConverter.ToInt32(Randb, 0);
value = value%(max + 1);
if (value < 0) value = -value;
return value;
}
public static int Next(int min, int max)
{
var value = Next(max - min) + min;
return value;
}
}
}
و برای تبدیل عدد تصادفی بدست آمده به متن نیز از این کلاس میتوان استفاده کرد که به طرز ساده ای این کار را انجام میدهد:
// NumberToString.cs
namespace PersianCaptchaHandler
{
public class NumberToString
{
#region Fields
private static readonly string[] Yakan = new[] { "صفر", "یک", "دو", "سه", "چهار", "پنج", "شش", "هفت", "هشت", "نه" };
private static readonly string[] Dahgan = new[] { "", "", "بیست", "سی", "چهل", "پنجاه", "شصت", "هفتاد", "هشتاد", "نود" };
private static readonly string[] Dahyek = new [] { "ده", "یازده", "دوازده", "سیزده", "چهارده", "پانزده", "شانزده", "هفده", "هجده", "نوزده" };
private static readonly string[] Sadgan = new [] { "", "یکصد", "دوصد", "سیصد", "چهارصد", "پانصد", "ششصد", "هفتصد", "هشتصد", "نهصد" };
private static readonly string[] Basex = new [] { "", "هزار", "میلیون", "میلیارد", "تریلیون" };
#endregion
private static string Getnum3(int num3)
{
var s = "";
var d12 = num3 % 100;
var d3 = num3 / 100;
if (d3 != 0)
s = Sadgan[d3] + " و ";
if ((d12 >= 10) && (d12 <= 19))
{
s = s + Dahyek[d12 - 10];
}
else
{
var d2 = d12 / 10;
if (d2 != 0)
s = s + Dahgan[d2] + " و ";
var d1 = d12 % 10;
if (d1 != 0)
s = s + Yakan[d1] + " و ";
s = s.Substring(0, s.Length - 3);
}
return s;
}
public static string ConvertIntNumberToFarsiAlphabatic(string snum)
{
var stotal = "";
if (snum == "0") return Yakan[0];
snum = snum.PadLeft(((snum.Length - 1) / 3 + 1) * 3, '0');
var l = snum.Length / 3 - 1;
for (var i = 0; i <= l; i++)
{
var b = int.Parse(snum.Substring(i * 3, 3));
if (b != 0)
stotal = stotal + Getnum3(b) + " " + Basex[l - i] + " و ";
}
stotal = stotal.Substring(0, stotal.Length - 3);
return stotal;
}
}
}
و برای کد کردن و دیکد کردن یعنی موارد سوم و چهارم مقاله قبلی، متن بدست آمده را که بعنوان قسمتی از آدرس تصویر در ادامه آدرس هندلر معرفی شده میآید تبدیل به یک string بی معنی برای بازدیدکننده میکند:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace PersianCaptchaHandler
{
public class Encryptor
{
#region constraints
private static string Password { get { return "Mehdi"; } }
private static string Salt { get { return "Payervand"; } }
#endregion
public static string Encrypt(string clearText)
{
// Turn text to bytes
var clearBytes = Encoding.Unicode.GetBytes(clearText);
var pdb = new PasswordDeriveBytes(Password, Encoding.Unicode.GetBytes(Salt));
var ms = new MemoryStream();
var alg = Rijndael.Create();
alg.Key = pdb.GetBytes(32);
alg.IV = pdb.GetBytes(16);
var cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
var encryptedData = ms.ToArray();
return Convert.ToBase64String(encryptedData);
}
public static string Decrypt(string cipherText)
{
// Convert text to byte
var cipherBytes = Convert.FromBase64String(cipherText);
var pdb = new PasswordDeriveBytes(Password, Encoding.Unicode.GetBytes(Salt));
var ms = new MemoryStream();
var alg = Rijndael.Create();
alg.Key = pdb.GetBytes(32);
alg.IV = pdb.GetBytes(16);
var cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
var decryptedData = ms.ToArray();
return Encoding.Unicode.GetString(decryptedData);
}
}
}
و نیز برای اعتبار سنجی عدد دریافتی از کاربر میتوان از عبارت با قاعده زیر استفاده کرد:
// Utils.cs
using System.Text.RegularExpressions;
namespace PersianCaptchaHandler
{
public class Utils
{
private static readonly Regex NumberMatch = new Regex(@"^([0-9]*|\d*\.\d{1}?\d*)$", RegexOptions.Compiled);
public static bool IsNumber(string number2Match)
{
return NumberMatch.IsMatch(number2Match);
}
}
}
برای استفاده نیز کافیست که هندلر مربوط به این کتابخانه را بطریق زیر در وب کانفیگ رجیستر کرد:
<add verb="GET" path="/captcha/" type="PersianCaptchaHandler.CaptchaHandler, PersianCaptchaHandler, Version=1.0.0.0, Culture=neutral" />
و در صفحه مورد نظرتان بطریق زیر میتوان از یک تگ تصویر برای نمایش تصویر تولیدی و از یک فیلد مخفی برای نگهداری مقدار کد شده معادل عدد تولیدی که در هنگام مقایسه با عدد ورودی کاربر مورد نیاز است استفاده شود:
<!-- ASPX -->
<dl>
<dt>تصویر امنیتی</dt>
<dd>
<asp:Image ID="imgCaptchaText" runat="server" AlternateText="CaptchaImage" />
<asp:HiddenField ID="hfCaptchaText" runat="server" />
<asp:ImageButton ID="btnRefreshCaptcha" runat="server" ImageUrl="/img/refresh.png"
OnClick="btnRefreshCaptcha_Click" />
<br />
<asp:TextBox ID="txtCaptcha" runat="server" AutoCompleteType="Disabled"></asp:TextBox>
</dd>
<dd>
<asp:Button ID="btnSubmit" runat="server" Text="ثبت" OnClick="btnSubmit_Click" />
</dd>
</dl>
<asp:Label ID="lblMessage" runat="server"></asp:Label>
در زمان لود صفحه، تصویر امنیتی مقدار دهی میشود و در زمان ورود عدد توسط کاربر با توجه به اینکه کاربر حتما باید عدد وارد کند با عبارت با قاعده این اعتبار سنجی انجام میشود:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
SetCaptcha();
}
private void SetCaptcha()
{
lblMessage.Text =
txtCaptcha.Text = string.Empty;
var newNumber =
RandomGenerator.Next(100, 999)
;
var farsiAlphabatic = NumberToString.ConvertIntNumberToFarsiAlphabatic(newNumber.ToString());
hfCaptchaText.Value =
HttpUtility
.UrlEncode(
Encryptor.Encrypt(
farsiAlphabatic
)
);
txtCaptcha.Text = string.Empty;
imgCaptchaText.ImageUrl =
"/captcha/?text=" + hfCaptchaText.Value;
}
و بعد از ورود عدد از سمت کاربر از متد تبدیل به حروف استفاده کرده و این مقدار تولیدی با مقدار فیلد مخفی مقایسه میشود:
private string GetCaptcha()
{
var farsiAlphabatic = NumberToString.ConvertIntNumberToFarsiAlphabatic(txtCaptcha.Text);
var encryptedString =
HttpUtility
.UrlEncode(
Encryptor.Encrypt(
farsiAlphabatic
)
);
return encryptedString;
}
private bool ValidateUserInputForLogin()
{
if (!Utils.IsNumber(txtCaptcha.Text))
{
lblMessage.Text = "تصویر امنیتی را بطور صحیح وارد نکرده اید";
return false;
}
var strGetCaptcha =
GetCaptcha();
var strDecodedVAlue =
hfCaptchaText.Value;
if (strDecodedVAlue != strGetCaptcha)
{
lblMessage.Text = "کلمه امنیتی اشتباه است";
SetCaptcha();
return false;
}
return true;
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
if (!ValidateUserInputForLogin()) return;
lblMessage.Text = "کلمه امنیتی درست است";
}
protected void btnRefreshCaptcha_Click(object sender, ImageClickEventArgs e)
{
SetCaptcha();
}
در آخر این
پروژه در کدپلکس قرار داده شده، و مشتاق نظرات و پیشنهادات شما هستم و نیز
نمونه مثال بالا ضمیمه شده است