ASP.NET MVC 의 CAPTCHR 구현에 대해 이야기 해 보자. 이는 ASP.NET MVC 6 을 기준으로 기술해 보겠다.
개발하기 전에 생각을 하면, 페이지 화면이 로딩 될때, CAPTCHA 태그가 이미지로 보여주면서 암호화 된 값이 페이지 안의 HIDDEN 필드에 설정을 한다.
그 다음 사용자가 페이지에서 저장을 하던가 SUBMIT 을 할때, 암호화 된 값과 사용자가 입력한 CAPTCHA 랑 비교해서 값을 비교하여 validation 을 할겁니다.
만약에 값이 동일하면 받아들이고, 그렇지 않으면 오류를 반환하면 된다. 여기서 사용할 암호화는 간단하게 base64 를 가지고 사용해 볼거다.
ASP.NET 5 는 drawing 에 대한 지원하는 nuget package 를 가지고 있지 않아서 , .NET System.Drawing namespace 을 사용해야 할 듯 싶다.
우선 .NET System.Drawing namespace 을 포함하기 위해 project.json 에 기술 해 봅시다.
"frameworks": {
"dnx451": {
"frameworkAssemblies": {
"System.ServiceModel": "4.0.0.0",
"System.Drawing": "4.0.0.0"
}
}
}
image content 형식 같은 이미지를 반환하는 기술 대신에, html 5 의 이미지 data 기능을 사용해 봅시다. 이는 이미지를 base64 문자열로 convert 해지고, 이미지의 속성인 src 에 설정하면 됩니다.
bitmap.Save(memoryStream, ImageFormat.Jpeg);
byte[] imageBytes = memoryStream.ToArray();
string base64String = Convert.ToBase64String(imageBytes);
output.Attributes["src"] = "https://t1.daumcdn.net/cfile/tistory/2521A74D56E6D71C37" + base64String;
이제 TagHelpers 를 사용해서 적용해 봅니다. (제일 하단에 코드 제공함)
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10" />
<div>
<img asp-captcha="true" class="img-thumbnail"/>
</div>
</div>
</div>
이를 validation 하기 위해선 Request.Form 의 collecton 을 활용해서 hidden field 값을 가져오도록 합시다.
var captchaImage = Context.Request.Form["__captcha_image"];
var encryptedString =
Convert.ToBase64String(UTF32Encoding.Unicode.GetBytes(user.Captcha));
if (captchaImage != encryptedString)
{
ModelState.AddModelError("", "Captcha is not matching.");
return View("SignUp");
}
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using System.Drawing;
using System.Drawing.Imaging;
using System;
using System.IO;
using Microsoft.AspNet.Mvc;
using System.Text;
namespace RSSReader.TagHelpers
{
[TargetElement("img", Attributes = "asp-captcha")]
public class CaptchaTagHelper : TagHelper
{
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
[HtmlAttributeName("asp-captcha")]
public bool EnableCaptcha { set; get; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (EnableCaptcha)
{
using (var memoryStream = new MemoryStream())
{
using (var bitmap = new Bitmap(150, 40, PixelFormat.Format32bppArgb))
{
using (var graphics = Graphics.FromImage(bitmap))
{
Rectangle rect = new Rectangle(0, 0, 149, 39);
graphics.FillRectangle(Brushes.White, rect);
Random r = new Random();
int startIndex = r.Next(1, 5);
int length = r.Next(5, 10);
string drawString = Guid.NewGuid().ToString().Replace("-", "0").Substring(startIndex, length);
Font drawFont = new Font("Arial", 16, FontStyle.Bold);
using (SolidBrush drawBrush = new SolidBrush(Color.Gray))
{
PointF drawPoint = new PointF(15, 10);
graphics.DrawRectangle(new Pen(Color.White, 0), rect);
graphics.DrawString(drawString, drawFont, drawBrush, drawPoint);
}
var encryptedString = Convert.ToBase64String(UTF32Encoding.Unicode.GetBytes(drawString));
output.PostElement.AppendFormat("<input type=\"hidden\"
name=\"__captcha_image\" id=\"__captcha_image\" value=\"{0}\" />",encryptedString);
}
bitmap.Save(memoryStream, ImageFormat.Jpeg);
byte[] imageBytes = memoryStream.ToArray();
string base64String = Convert.ToBase64String(imageBytes);
output.Attributes["src"] = "https://t1.daumcdn.net/cfile/tistory/2561B84856E6D71D1E" + base64String;
}
}
}
}
}
}