재우니의 블로그

 

 

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;
                        
                    }
                }
            }
        }
    }
}