# Login的驗證碼 1. 純JS版本 無後端檢查,容易被繞過,若有不支援外網,則無法正確載入字體暨無法產生驗證碼。 ```javascript= $(document).ready(function () { var code; var f = new FontFace("Cabin Sketch", "url(https://fonts.gstatic.com/s/cabinsketch/v14/QGYpz_kZZAGCONcK2A4bGOj8mNhNy_r-Kw.woff2)"); f.load().then(function (f) { document.fonts.add(f); createCaptcha(); }); $('#reflash_cpatcha').click(function () { createCaptcha(); }); $('.btn-member').click(function (e) { if (validateCaptcha()) { $('#cpatcha_notice_error').hide(); $('#account').submit(); } }) }); function createCaptcha() { //clear the contents of captcha div first document.getElementById('captcha').innerHTML = ""; var charsArray = "23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; var lengthOtp = 6; var captcha = []; for (var i = 0; i < lengthOtp; i++) { //below code will not allow Repetition of Characters var index = Math.floor(Math.random() * charsArray.length + 1); //get the next character from the array if (captcha.indexOf(charsArray[index]) == -1) captcha.push(charsArray[index]); else i--; } var canv = document.createElement("canvas"); canv.id = "captcha"; canv.width = 140; canv.height = 40; var ctx = canv.getContext("2d"); ctx.font = "32px Cabin Sketch"; ctx.strokeText(captcha.join(""), 0, 30); //storing captcha so that can validate you can save it somewhere else according to your specific requirements code = captcha.join(""); document.getElementById("captcha").appendChild(canv); // adds the canvas to the body element } function validateCaptcha() { if (document.getElementById("cpatchaTextBox").value != code) { $('#cpatcha_notice_error').show(); return false; } else { return true; } } ``` 2. 後端產生驗證碼圖片版本 使用Drawing產生圖片,隨機一組數字之後將其儲存至session 並於後端post login時,檢查使用者輸入的CaptchaCode 及session中的code是否一致 若不一致則 AddModelError 並 return page() 參考使用 https://blog.johnwu.cc/article/asp-net-core-angular-4-%E6%95%99%E5%AD%B8-captcha.html 但是套件部分需改為以下 ```csharp= <PackageReference Include="runtime.osx.10.10-x64.CoreCompat.System.Drawing" Version="6.0.5.128" /> <PackageReference Include="System.Drawing.Common" Version="5.0.0" /> ``` **System.Drawing.Common的版本要視目前的.net版本進行調整** **runtime.osx.10.10-x64是用於mac開發環境會有以下問題發生** > DllNotFoundException: Unable to load shared library 'libgdiplus' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: dlopen(liblibgdiplus, 1): image not found --- Login.cshtml.cs中,InputModel新增了CaptchaCode的field檢查 ```csharp= public class InputModel { [Required(ErrorMessage = "此欄位為必填")] [EmailAddress(ErrorMessage = "請輸入正確的電子信箱格式")] public string Email { get; set; } [Required(ErrorMessage = "此欄位為必填")] [DataType(DataType.Password, ErrorMessage = "密碼須包含至少各一個數字、大小寫字母及符號")] public string Password { get; set; } [Required(ErrorMessage = "此欄位為必填")] [StringLength(4)] public string CaptchaCode { get; set; } [Display(Name = "Remember me?")] public bool RememberMe { get; set; } } ``` 同Login.cshtml.cs中 於ModelState.IsValid後,檢查使用者輸入的驗證碼是否跟session中的相同 如果不同就 addModelError 並導回至原本的登入頁面 ```csharp= if (ModelState.IsValid) { string CaptchaHash = HttpContext.Session.GetString("CaptchaCode"); if (!Input.CaptchaCode.Equals(CaptchaHash)) { ModelState.AddModelError("Input.CaptchaCode", "驗證碼輸入錯誤"); TempData["Errors"] = "驗證碼輸入錯誤"; if (HttpContext.Request.Headers["Referer"].ToString().Contains("AECadmin")) return Redirect(HttpContext.Request.Headers["Referer"]); else return Page(); } ... ``` --- 前端 表單新增了驗證碼的區塊,修正改為以img方式載入後端所產生的驗證碼圖片,驗證碼圖片於產生時,會將隨機出來的cpatcha code同時存在session中 ```htmlembedded= <div class="form_grp"> <div class="form_title">驗證碼</div> <div class="form_content"> <div class="form_inline"> <div> <img id="captcha" src="/captcha" alt="輸入驗證碼"> </div> <input asp-for="Input.CaptchaCode" type="text" placeholder="請輸入驗證碼" /> <button class="btn" type="button" id="reflash_cpatcha"><i class="i_reflash"></i>重新整理</button> </div> <span class="notice_error" asp-validation-for="Input.CaptchaCode"></span> </div> </div> ``` CaptchaBLL 為 [這篇文章](https://blog.johnwu.cc/article/asp-net-core-angular-4-%E6%95%99%E5%AD%B8-captcha.html) 作者所寫的一個圖片產生的BLL,須安裝package才可以使用,安裝package時要特別注意version,CaptchaCode可以加密後放進Session或不加密也可。 於CaptchaBLL中可以修改要random的文字,及如何混淆圖片等方法。 ```csharp= [Route("captcha")] [HttpGet] public ActionResult GetCaptcha() { // 隨機產生四個字元 var randomText = CaptchaBLL.GenerateRandomText(4); HttpContext.Session.SetString("CaptchaCode", randomText); // 回傳 gif 圖檔 return File(captchaBLL.GenerateCaptchaImage(randomText), "image/gif"); } ``` 刷新圖片方式改為以jquery方式,修改圖片src,並加上時間參數避免img吃到庫存圖片 ```javascript= <script> $(document).ready(function () { $('#reflash_cpatcha').click(function () { refreshCaptcha(); }); }); function refreshCaptcha(){ $('#captcha').attr("src",$("#captcha").attr("src")+"?timestamp=" + new Date().getTime()); } </script> ```