This is a work in progress, and for my furthering my notes. Your comments are welcome.
I decide to code my personal Captcha Image. My Captcha idea is new, which probably can be found somewhere on the internet, however I tweaked the idea into a .NET Class (CaptchaDisplay class within my Captcha namespace)..
This Captcha class is instantiated from two areas. It is instantiated within an User control and a Generic Handler.
The User control’s code behind handles generating the Captcha characters, while the Generic Handler handles creating the Image from characters passed it through a query string. The characters are generated from the User Control.
I added an external appSettings page to the Web.config:
<appSettings file="appSettings.config"/>
Within the appSettings.config file:
<?xml version="1.0"?>
<appSettings>
<add key="Tumblers" value="j(mW*)UYRvxQ$4^54hT" />
</appSettings>
The User Control is shown below:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="CaptchaControl.ascx.cs"
Inherits="CaptchaHandler.CaptchaControl" %>
<asp:HiddenField ID="captchaKey" ClientIDMode="Static" runat="server" />
<asp:Panel ID="Captcha" ClientIDMode="Static" runat="server">
</asp:Panel>
and its Code Behind page:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using deDogs.Captcha;
namespace CaptchaHandler
{
public partial class CaptchaControl : System.Web.UI.UserControl
{
private int _width;
public int Width
{
set
{
_width = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
using (CaptchaDisplay captcha = new CaptchaDisplay(_width))
{
captcha.GetCharacters();
string[] c = captcha.characters;
string key =new string(captcha.SaltKey(c));
string encodeKey = HttpUtility.UrlEncode(key);
captchaKey.Value = encodeKey;
Image img = new Image();
img.ImageUrl = "Captcha.ashx?key=" + encodeKey + "&w=" + _width;
img.AlternateText = "Captcha Image";
Captcha.Controls.Add(img);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using deDogs.Captcha;
using System.Drawing.Imaging;
namespace CaptchaHandler
{
/// <summary>
/// Display Captcha Characters Image
/// </summary>
public class CaptchaHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
HttpContext.Current.Response.ContentType = "image/jpeg";
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.BufferOutput = false;
try
{
Int32 width = Convert.ToInt32(HttpContext.Current.Request["w"]);
if (width > 0) {
using (CaptchaDisplay captcha = new CaptchaDisplay(width))
{
try
{
string key = HttpUtility.UrlDecode(HttpContext.Current.Request["key"].ToString());
if (key != "")
{
//Decrypt key
char[] unSalted = captcha.SaltKey(key.ToCharArray());
captcha.characters = unSalted.SelectMany(x => new string[] { x.ToString() }).ToArray();
captcha.CaptchaImage();
captcha._bitMap.Save(HttpContext.Current.Response.OutputStream, ImageFormat.Jpeg);
}
}
catch (Exception)
{
throw;
}
}
}
}
catch (Exception)
{
throw;
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
Finally the Captch class shown below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Web;
using deDogs.Validation;
using System.Configuration;
using System.Web.SessionState;
namespace deDogs
{
namespace Captcha
{
public class CaptchaDisplay : IDisposable
{
public CaptchaDisplay(int width)
{
this.fonts = new string[] { "Times New Roman", "Georgia", "Arial", "Comic Sans MS" };
this.sizes = new int[] { 36, 48, 60, 72 };
this.characterSet = @"ABDEFGHJKLMNQRTYZabdefghkmnqryz23456789*#@%$";
this.colr = new Brush[] { Brushes.Black };
this.backgroundColor = Brushes.White;
this.salt = new char[] { 'g', '-', '$', 'k', '5', '?', 'O' };
this._bitMap = new Bitmap(width, (int)(width * .32F));
}
public CaptchaDisplay() { }
protected class CaptchaChars
{
public CaptchaChars() { }
public CaptchaChars(string character, CaptchaRegion region)
{
this.character = character;
this.region = region;
this.memoryStream = new MemoryStream();
}
public MemoryStream memoryStream { get; set; }
public string character;
public CaptchaRegion region;
public int angle { get; set; }
}
protected class CaptchaRegion
{
public CaptchaRegion() { }
public CaptchaRegion(float x, float y, float width, float height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public float x { get; set; }
public float y { get; set; }
public float width { get; set; }
public float height { get; set; }
}
public Bitmap _bitMap;
Brush color(Random rnd)
{
return colr[rnd.Next(0, colr.Length)];
}
PinTumblerLock pinTumblerLock;
public char[] salt { get; private set; }
public string[] characters;
public string characterSet;
public string[] fonts { get; set; }
public int[] sizes { get; set; }
public Brush[] colr;
public Brush backgroundColor;
public PinTumblerLock LockKey
{
get
{
return pinTumblerLock;
}
set
{
pinTumblerLock = value;
}
}
public void GetCharacters()
{
Random rnd = new Random();
int numChars = rnd.Next(4, 7);
this.characters = new string[numChars];
for (var i = 0; i < numChars; i++)
{
this.characters[i] = this.characterSet.Substring(rnd.Next(0, this.characterSet.Length), 1);
}
}
public void CaptchaImage()
{
Random rnd = new Random();
CaptchaChars captchaChar;
List<CaptchaChars> captchaChars = new List<CaptchaChars>();
CharacterRange[] cR = { new CharacterRange(0, 1) };
StringFormatFlags flags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
StringFormat sF = new StringFormat(flags);
sF.SetMeasurableCharacterRanges(cR);
RectangleF displayRectangleF = new RectangleF(0, 0, this._bitMap.Width, this._bitMap.Height);
Font largeFont;
int width = 80, height = 80;
for (var c = 0; c < characters.Length; c++)
{
HttpContext.Current.Trace.Write(characters[c]);
int angle = rnd.Next(0, 30);
angle = (angle % 2 == 0) ? -angle : angle;
captchaChar = new CaptchaChars(characters[c], new CaptchaRegion(0, 0, width, height));
captchaChar.angle = angle;
using (Bitmap bitMap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
using (Graphics g = Graphics.FromImage(bitMap))
{
Rectangle rect = new Rectangle(new Point(0, 0), new Size(bitMap.Width, bitMap.Height));
g.FillRectangle(this.backgroundColor, rect);
largeFont = new Font(this.fonts[rnd.Next(0, this.fonts.Length)], this.sizes[rnd.Next(0, this.sizes.Length)], GraphicsUnit.Pixel);
Region[] charRegion = g.MeasureCharacterRanges(characters[c], largeFont, displayRectangleF, sF);
g.DrawString(characters[c], largeFont, colr[rnd.Next(0, colr.Length)], displayRectangleF);
bitMap.Save(captchaChar.memoryStream, ImageFormat.Jpeg);
captchaChars.Add(captchaChar);
}
}
}
this.Display(captchaChars);
}
void Display(List<CaptchaChars> c)
{
using (Graphics mainGraphic = Graphics.FromImage(this._bitMap))
{
StringBuilder charSet = new StringBuilder();
Rectangle rect = new Rectangle(new Point(0, 0), new Size(this._bitMap.Width, this._bitMap.Height));
mainGraphic.FillRectangle(this.backgroundColor, rect);
int newX = 0;
float bwidth, bheight = 0;
foreach (CaptchaChars m in c)
{
bwidth = m.region.width;
bheight = m.region.height;
Bitmap bitMap = new Bitmap(m.memoryStream);
using (Graphics g = Graphics.FromImage(bitMap))
{
Rotate(m, g);
g.DrawImage(bitMap, 0, 0);
mainGraphic.DrawImage(bitMap, newX, 0);
newX += (int)m.region.width;
charSet.Append(m.character);
}
}
//Crop image bounding selected characters
this._bitMap = this._bitMap.Clone(new Rectangle(0, 0, newX, (int)bheight), PixelFormat.Format32bppArgb);
Lockup(charSet.ToString());
Distort();
Decorate();
}
}
private void Lockup(string key)
{
PinTumblerLock lockup = new PinTumblerLock(key, (ConfigurationManager.AppSettings["Tumblers"]));
}
public char[] SaltKey(char[] key)
{
int len = key.Length;
for (int i = 0; i < len; i++)
{
key[i] ^= this.salt[i];
}
return key;
}
public char[] SaltKey(string[] key)
{
char[] c = string.Join(string.Empty, key).ToCharArray();
return this.SaltKey(c);
}
void Rotate(CaptchaChars c, Graphics g)
{
g.TranslateTransform(c.region.width / 2, c.region.height / 2);
g.RotateTransform(c.angle);
g.TranslateTransform(-c.region.width / 2, -c.region.height / 2);
}
protected void Distort()
{
Random rnd = new Random();
double distort = rnd.Next(5, 20) * (rnd.Next(10) == 1 ? 1 : -1);
int width = this._bitMap.Width;
int height = this._bitMap.Height;
using (Bitmap copy = (Bitmap)this._bitMap.Clone())
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// Adds a simple wave
int newX = (int)(x + (distort * Math.Sin(Math.PI * y / 84.0)));
int newY = (int)(y + (distort * Math.Cos(Math.PI * x / 44.0)));
if (newX < 0 || newX >= width) newX = 0;
if (newY < 0 || newY >= height) newY = 0;
this._bitMap.SetPixel(x, y, copy.GetPixel(newX, newY));
}
}
}
}
protected void Decorate()
{
using (Graphics g = Graphics.FromImage(this._bitMap))
{
Random rnd = new Random();
int width = this._bitMap.Width;
int height = this._bitMap.Height;
// Draw lines between original points to screen.
g.DrawCurve(new Pen(color(rnd), 3.0F), Points(5, rnd).ToArray());
foreach (Point pt in Points(5, rnd))
{
g.DrawRectangle(new Pen(color(rnd), 3.0F), new Rectangle(pt, new Size(rnd.Next(5, 20), rnd.Next(5, 20))));
}
}
}
protected List<Point> Points(int pts, Random rnd)
{
List<Point> points = new List<Point>();
int width = this._bitMap.Width - 20;
int height = this._bitMap.Height - 20;
for (int i = 0; i < pts; i++)
{
points.Add(new Point(rnd.Next(0, width), rnd.Next(0, height)));
}
return points;
}
public void Dispose()
{
this._bitMap.Dispose();
this._bitMap = null;
}
}
}
}
No comments:
Post a Comment