Поиск на сайте
Главная Журнал Форум Wiki DRKB Страны мира

Картинка КАПЧА

Идея капчи заключается в том, чтобы защитить сайт от спамеров, использующих веб-ботов при регистрации новых учётных записей, отправки комментариев через веб-формы и т.д. Пользователю показывается картинка с искажённым текстом (для защиты от программ оптического распознавания). Большинство пользователей легко смогут распознать искажённый текст и ввести его для проверки, а программы - нет.

Использование кода

ZIP файл (который можно скачать в конце статьи) содержит исходники одного класса и двух веб-форм. Просто создайте новый проект и вставьте в него эти элементы.

Файлы

• CaptchaImage.cs - описывает объект CapchaImage, который создает само изображение.
• Default.aspx, Default.aspx.cs - примеры веб-формы.
• JpegImage.aspx, JpegImage.aspx.cs - веб-форма предназначенная для вывода JPEG изображения, а не HTML.

Теперь подробнее рассмотрим каждый компонент и его назначение.

CaptchaImage.cs

Объект CaptchaImage создает изображение для текста, который будет отображаться, размеры изображения и, при необходимости, используемый шрифт.

Основной элемент, это метод GenerateImage(), приведённый ниже, который создает растровое изображение заданной ширины и высоты. Этот метод вызывается из конструктора CaptchaImage, поэтому картинка генерируется при создании нового экземпляра объекта. При создании картинки, в первую очередь заполняется фон, используя кисть HatchBrush (чем "грязнее" будет изображение, тем труднее его будет распознать оптическим распознавалкам).

Чтобы вписать текст в картику, необходимо определить начальный размер шрифта на основе высоты изображения и использовать метод Graphics.MeasureString(), чтобы вычислить длину отображаемого текста. Если текст превышает размеры изображения, то уменьшаем размер шрифта и проверяем снова и снова, пока подходящий размер шрифта не будет найден.

    // ====================================================================
    // Создание битмап-картинки.
    // ====================================================================

    private void GenerateImage()
    {
      // Создаём новое 32-битное битмап-изображение.

      Bitmap bitmap = new Bitmap(
        this.width,
        this.height,
        PixelFormat.Format32bppArgb);
 
      // Создаём графический объект для рисования.

      Graphics g = Graphics.FromImage(bitmap);
      g.SmoothingMode = SmoothingMode.AntiAlias;
      Rectangle rect = new Rectangle(0, 0, this.width, this.height);
 
      // Заполняем фон.

      HatchBrush hatchBrush = new HatchBrush(
        HatchStyle.SmallConfetti,
        Color.LightGray,
        Color.White);
      g.FillRectangle(hatchBrush, rect);
 
      // Устанавливаем шрифт текста.

      SizeF size;
      float fontSize = rect.Height + 1;
      Font font;
      // Настраиваем размер шрифта, чтобы текст вписался в картинку.

      do
      {
        fontSize--;
        font = new Font(
          this.familyName,
          fontSize,
          FontStyle.Bold);
        size = g.MeasureString(this.text, font);
      } while (size.Width > rect.Width);
 
      // Устанавливаем формат текста.

      StringFormat format = new StringFormat();
      format.Alignment = StringAlignment.Center;
      format.LineAlignment = StringAlignment.Center;
 
      // Создаём путь используя текст и вписываем его рандомно.

      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);
 
      // Рисуем текст.

      hatchBrush = new HatchBrush(
        HatchStyle.LargeConfetti,
        Color.LightGray,
        Color.DarkGray);
      g.FillPath(hatchBrush, path);
 
      // Добавляем случайный шум.

      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);
      }
 
      // Освобождаем ресурсы.

      font.Dispose();
      hatchBrush.Dispose();
      g.Dispose();
 
      // Устанавливаем изображение.

      this.image = bitmap;
    }

После того как шрифт установлен, используем GraphicsPath(), чтобы преобразовать текст в набор линий и кривых. Чтобы исказить изображение текста, используем метод GraphicsPath.Warp() с некоторыми случайно сгенерированными значениями. Потом опять используем "грязную" кисть чтобы сам текст был не только искажённым, но и загрязнённым.

Для полного искажения добавляем небольшие случайные помарки к общему изображению. Можно экспериментировать с любыми типами помарок, только не перестарайтесь, иначе некоторые посетители с нарушениями зрения не смогут разобрать текст на картинке.

Default.aspx

Это самый простой образец веб-формы, которая содержит всего несколько основных элементов: тег картинки < IMG >, текстовое поле и кнопку "Отправить".

    <form id="Default" method="post" runat="server">
      <img src="JpegImage.aspx"><br>
      <p>
        <strong>Введите код, изображённый на картинке:</strong><br>
        <asp:TextBox id="CodeNumberTextBox" runat="server"></asp:TextBox>
        <asp:Button id="SubmitButton" runat="server" Text="Отправить">
        </asp:Button><br>
      </p>
      <p>
        <em class="notice">
          (Внимание: Если Вы не смогли распознать код на картинке,<br>
          обновите страницу для смены кода.)</em>
      </p>
      <p><asp:Label id="MessageLabel" runat="server"></asp:Label></p>
    </form>

Обратите внимание, что атрибут SRC тэга < IMG > указывает на веб-форму JpegImage.aspx.

Код для Default.aspx просто генерирует случайную строку текста для картинки и подтверждает, что этот текст, действительно введен пользователем при сабмите формы. Основной момент заключается в том, что текстовой строка сохраняется в объекте Session.

    private void Page_Load(object sender, System.EventArgs e)
    {
      if (!this.IsPostBack)
 
        // Генерируем случайный код и сохраняем его объекте Сессии (Session).

        this.Session["CaptchaImageText"] = GenerateRandomCode();
 
      else
      {
        // При постбэке проверяем ввод пользователя.

        if (this.CodeNumberTextBox.Text ==
          this.Session["CaptchaImageText"].ToString())
        {
          // Показываем информационное сообщение.

          this.MessageLabel.CssClass = "info";
          this.MessageLabel.Text = "Correct!";
        }
        else
        {
          // Показываем сообщение об ошибке.

          this.MessageLabel.CssClass = "error";
          this.MessageLabel.Text = "ERROR: Incorrect, try again.";
 
          // Очищаем ввод и создаём новый случайный код.

          this.CodeNumberTextBox.Text = "";
          this.Session["CaptchaImageText"] = GenerateRandomCode();
        }
      }
    }

Хранения текстовой строки в объекте Session сделано для того, чтобы она была доступна для JpegImage.aspx.

JpegImage.aspx

Для этой веб-формы не нужен HTML-код - она просто генерирует JPEG-картинку (весь html-код, который там есть сгенерирован по умолчанию Вижуал Студией при создании файла).

В коде мы сперва создаем объект CaptchaImage (при создании объекта происходит генерация самой картинки), используя текст извлекаемый из объекта Session.

    private void Page_Load(object sender, System.EventArgs e)
    {
      // Генерируем КАПЧА-картинку, используя текст, сохранённый в объекте Session.

      CaptchaImage ci = new CaptchaImage(
        this.Session["CaptchaImageText"].ToString(),
        200, 50, "Century Schoolbook");
 
      // Прописываем в заголовках тип картинки JPEG.

      this.Response.Clear();
      this.Response.ContentType = "image/jpeg";
 
      // Записываем изображение выходной поток в формате JPEG.

      ci.Image.Save(this.Response.OutputStream, ImageFormat.Jpeg);
 
      // Освобождаем объект капча-картинки.

      ci.Dispose();
    }

Затем меняем заголовки HTTP-ответа на Content-type="image/jpeg", чтобы браузер клиента знал, что мы посылаем ему изображение.

В заключании получаем растровое изображение с CaptchaImage.Image и записываем его в поток HTTP-ответа в формате JPEG. Для этого используется метод Save(). Можно так же установить любые другие форматы изображения, естевственно понадобится изменить заголовок Content-type соответствующим образом.

Важный момент

Так как класс CaptchaImage содержит объект Bitmap, то в конце вызывается метод Dispose(), чтобы освободить ресурсы когда CaptchaImage будет уничтожен.

Скачать исходник - 6 кб




Основные разделы сайта


 

Реклама