Doğrulama Kodu (CAPTCHA) Kullanımı

Bazı sitelerde karşınıza çıkan bu resimler ne anlama geliyor ve biz nasıl kullanabiliriz?

CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart), Carnegie Mellon School of Computer Science tarafından geliştirilen bir projedir.

Projenin amacı bilgisayar ile insanların davranışlarının ayırt edilmesidir.

Bir başka deyişle, insan olduğunuzu web sitenize ispat etmektir. :)

CAPTCHA uygulamalarına çoğu web sayfalarında rastlamak mümkündür.

Üyelik formlarında, iletişim sayfalarında, vb. sayfalarda rastgele karakterler barındıran resim gösterilerek, formu dolduran kişiden bu resmin üzerinde yazan karakterleri girmesi istenir.

Buradaki basit mantık; resimde sadece insan tarafından okunabilecek, bir program tarafından okunması zor olan (bkz: OCR programları) bir sözcük oluşturmaktır.

Gelelim bu uygulamayı ASP.NET projelerinde nasıl kullanabileceğimize.

Bize yardımcı olması açısından önce bir class yazalım, adı da DogrulamaKodu olsun:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;

namespace DevrimAltinkurt
{
    /// <summary>
    /// Summary description for CaptchaImage.
    /// </summary>
    public class DogrulamaKodu
    {
        // Public properties (all read-only).
        public string Text
        {
            get { return this.text; }
        }
        public Bitmap Image
        {
            get { return this.image; }
        }
        public int Width
        {
            get { return this.width; }
        }
        public int Height
        {
            get { return this.height; }
        }

        // Internal properties.
        private string text;
        private int width;
        private int height;
        private string familyName;
        private Bitmap image;

        // For generating random numbers.
        private Random random = new Random();

        public DogrulamaKodu(int width, int height)
        {
            this.text = RastgeleKodUretici();
            System.Web.HttpContext.Current.Session["CaptchaMetin"] = this.text;
            this.SetDimensions(width, height);
            this.ResimOlustur();
        }

        // ====================================================================
        // Initializes a new instance of the CaptchaImage class using the
        // specified text, width and height.
        // ====================================================================
        public DogrulamaKodu(string s, int width, int height)
        {
            this.text = s;
            this.SetDimensions(width, height);
            this.ResimOlustur();
        }

        // ====================================================================
        // Initializes a new instance of the CaptchaImage class using the
        // specified text, width, height and font family.
        // ====================================================================
        public DogrulamaKodu(string s, int width, int height, string familyName)
        {
            this.text = s;
            this.SetDimensions(width, height);
            this.SetFamilyName(familyName);
            this.ResimOlustur();
        }

        // ====================================================================
        // This member overrides Object.Finalize.
        // ====================================================================
        ~DogrulamaKodu()
        {
            Dispose(false);
        }

        // ====================================================================
        // Releases all resources used by this object.
        // ====================================================================
        public void Dispose()
        {
            GC.SuppressFinalize(this);
            this.Dispose(true);
        }

        // ====================================================================
        // Custom Dispose method to clean up unmanaged resources.
        // ====================================================================
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
                // Dispose of the bitmap.
                this.image.Dispose();
        }

        // ====================================================================
        // Sets the image width and height.
        // ====================================================================
        private void SetDimensions(int width, int height)
        {
            // Check the width and height.
            if (width <= 0)
                throw new ArgumentOutOfRangeException("width", width, "Argument out of range, must be greater than zero.");
            if (width <= 0)
                throw new ArgumentOutOfRangeException("height", height, "Argument out of range, must be greater than zero.");
            this.width = width;
            this.height = height;
        }

        // ====================================================================
        // Sets the font used for the image text.
        // ====================================================================
        private void SetFamilyName(string familyName)
        {
            // If the named font is not installed, default to a system font.
            try
            {
                Font font = new Font(this.familyName, 12F);
                this.familyName = familyName;
                font.Dispose();
            }
            catch (Exception)
            {
                this.familyName = System.Drawing.FontFamily.GenericSerif.Name;
            }
        }

        // ====================================================================
        // Creates the bitmap image.
        // ====================================================================
        private void ResimOlustur()
        {
            // Create a new 32-bit bitmap image.
            Bitmap bitmap = new Bitmap(this.width, this.height, PixelFormat.Format32bppArgb);

            // Create a graphics object for drawing.
            Graphics g = Graphics.FromImage(bitmap);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            Rectangle rect = new Rectangle(0, 0, this.width, this.height);

            // Fill in the background.
            HatchBrush hatchBrush = new HatchBrush(HatchStyle.SmallConfetti, Color.LightGray, Color.White);
            g.FillRectangle(hatchBrush, rect);

            // Set up the text font.
            SizeF size;
            float fontSize = rect.Height + 1;
            Font font;
            // Adjust the font size until the text fits within the image.
            do
            {
                fontSize--;
                font = new Font(this.familyName, fontSize, FontStyle.Bold);
                size = g.MeasureString(this.text, font);
            } while (size.Width > rect.Width);

            // Set up the text format.
            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;

            // Create a path using the text and warp it randomly.
            GraphicsPath path = new GraphicsPath();
            path.AddString(this.text, font.FontFamily, (int)font.Style, font.Size, rect, format);
            float v = 4F;
            PointF[] points =
   {
    new PointF(this.random.Next(rect.Width) / v, this.random.Next(rect.Height) / v),
    new PointF(rect.Width - this.random.Next(rect.Width) / v, this.random.Next(rect.Height) / v),
    new PointF(this.random.Next(rect.Width) / v, rect.Height - this.random.Next(rect.Height) / v),
    new PointF(rect.Width - this.random.Next(rect.Width) / v, rect.Height - this.random.Next(rect.Height) / v)
   };
            Matrix matrix = new Matrix();
            matrix.Translate(0F, 0F);
            path.Warp(points, rect, matrix, WarpMode.Perspective, 0F);

            // Draw the text.
            hatchBrush = new HatchBrush(HatchStyle.LargeConfetti, Color.LightGray, Color.DarkGray);
            g.FillPath(hatchBrush, path);

            // Add some random noise.
            int m = Math.Max(rect.Width, rect.Height);
            for (int i = 0; i < (int)(rect.Width * rect.Height / 30F); i++)
            {
                int x = this.random.Next(rect.Width);
                int y = this.random.Next(rect.Height);
                int w = this.random.Next(m / 50);
                int h = this.random.Next(m / 50);
                g.FillEllipse(hatchBrush, x, y, w, h);
            }

            // Clean up.
            font.Dispose();
            hatchBrush.Dispose();
            g.Dispose();

            // Set the image.
            this.image = bitmap;
        }

        public static string RastgeleKodUretici()
        {
            Random rnd = new Random();
            string s = "";
            for (int i = 0; i < 6; i++)
            {
                // 0 - karakter
                // 1 - rakam
                int a = rnd.Next(2);
                switch (a)
                {
                    case 0:
                        // A-Z arası bir harf
                        char c = Convert.ToChar(65 + rnd.Next(26));
                        s += Convert.ToString(c);
                        break;
                    default:
                        // 0-9 arası bir rakam
                        s += rnd.Next(10).ToString();
                        break;
                }
            }
            return s;
        }
    }
}

 Yaptığı işler:

  • Rastgele metin üretmek,
  • Verilen metne göre, üzerindeki karakterlerin okunması zor bir resim (Bitmap) oluşturmak.

Doğrulama kodunu web sayfamızda gösterebilmek için bir GenericHandler oluşturalım, adı da JpegImage.ashx olsun.

<%@ WebHandler Language="C#" Class="JpegImage" %>

using System;
using System.Web;
using DevrimAltinkurt;
public class JpegImage : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
    public void ProcessRequest(HttpContext context)
    {
        // session daki bilgi ile resmi oluşturuyoruz
        DogrulamaKodu ci =
            new DogrulamaKodu(context.Session["CaptchaMetin"].ToString(), 200, 40);

        context.Response.Clear();
        context.Response.ContentType = "image/jpeg";

        ci.Image.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);

        ci.Dispose();
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }

}

Yaptığı iş basit; verilen bilgiye göre yukarıdaki sınıfı da kullanarak ekrana resmi basmak.

İpucu: GenericHandler içinden session a erişim sağlayabilmek için handlerimizi System.Web.SessionState.IRequiresSessionState interface inden türetiyoruz.

Tabiki sırada, bu GenericHandler dosyasını da kullanarak bir form oluşturmak var.

Önce design tarafı:

<table>
    <tr>
        <td>
            Adınız
        </td>
        <td>
            <asp:TextBox ID="txtAd" runat="server" />
        </td>
    </tr>
    <tr>
        <td>
            Resimdeki metni yazınız
        </td>
        <td>
            <asp:TextBox ID="txtCaptcha" runat="server" AutoCompleteType="Disabled" />
            <br />
            <asp:Image ID="imgCaptcha" runat="server" ImageUrl="~/JpegImage.ashx" />
        </td>
    </tr>
    <tr>
        <td>
        </td>
        <td>
            <asp:Button ID="btnDevamEt" runat="server" OnClick="btnDevamEt_Click" Text="Gönder »" />
            <asp:Label ID="lblMesaj" runat="server" Visible="false" />
        </td>
    </tr>
</table>

Ad alanını öylesine ekledim, formda başka bilgiler de girilsin istedim.

imgCaptcha nesnesine dikkat edin, resim yolu olarak GenericHandler dosyamızı veriyoruz.

Kod tarafı:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        // Sayfaya ilk girişte kodu oluştur
        // Bilgiyi session da tutuyoruz
        Session["CaptchaMetin"] = DevrimAltinkurt.DogrulamaKodu.RastgeleKodUretici();
    }
}

protected void btnDevamEt_Click(object sender, EventArgs e)
{
    lblMesaj.Visible = false;

    // Kod hatalı girilmişse, yeni kod oluştur
    if (!Session["CaptchaMetin"].ToString().Equals(txtCaptcha.Text))
    {
        txtCaptcha.Text = "";
        Session["CaptchaMetin"] = DevrimAltinkurt.DogrulamaKodu.RastgeleKodUretici();
        lblMesaj.Visible = true;
        lblMesaj.Text = "Doğrulama kodu hatalı";
        lblMesaj.ForeColor = System.Drawing.Color.Red;
        txtCaptcha.Focus();
        return;
    }
    // Kod doğru girişmişse, gerisi size kalmış :)
    string ad = txtAd.Text;
    lblMesaj.Visible = true;
    lblMesaj.Text = string.Format("Sevgili <b>{0}</b>, doğrulama kodunu doğru girdin :)", ad);
    lblMesaj.ForeColor = System.Drawing.Color.Green;
}

Sayfaya ilk girişte, rastgele bir metin üretip session a atıyoruz. Butona tıklandıktan sonra da textboxa girilen bilgi ile session ı karşılaştırıyoruz. (İşte bütün mesele de bu)

Aynı ise sorun yok, gerisi size kalmış.

Ama farklı girilmişse, yeniden rastgele metin üretip sessiona atıyoruz. Kullanıcı metni doğru yazana kadar da işlemi tekrar ettiriyoruz.

Böylece, formu dolduranın insan olduğundan emin olmuş oluyoruz. (Bu cümlede insan kelimesini kullanmak hoşuma gidiyor, siz bunu "bot olmadığından emin oluyoruz" şeklinde okuyabilirsiniz.)

 

Uygulama videosu:

 

Proje örneğini indirmek için buraya tıklayabilirsiniz.

İlgili linkler: