首頁 後端開發 C#.Net教程 MVC使用極驗驗證製作登入驗證碼

MVC使用極驗驗證製作登入驗證碼

Jan 13, 2017 pm 03:23 PM

       在之前的專案中,如果有需要使用驗證碼,基本上都是自己用GDI+畫圖出來,簡單好用,但是卻也存在了一些小問題,首先若較少干擾線,則安全性不是很高,驗證碼容易被機器識別,若多畫太多幹擾線條,機器人辨識率下降的同時,人眼的辨識率也同步下降(震驚哭)。更重要的是,GDI+繪製的驗證碼一般來說也不會很美觀,如果做一個酷炫的登陸介面卻配了這樣一個驗證碼,畫風詭異,醜到極致。

      再後來瀏覽網頁的過程中,發現很多很多網站項目中都使用了一種叫極驗驗證的驗證碼,採用移動滑塊的方式進行驗證,方便美觀。而一番搜尋之後了解到,官方提供的免費版也足以應付我手頭上的大多數項目了,不禁想把在MVC學習過程中試著使用極驗驗證來作為登錄的驗證碼。

      極驗官方提供了C#的SDK和Demo供開發者參考,不過是Webform版本的,可讀性不是很高,而現在使用Webform進行網站開發的也基本上消失了,我將在官方Webform代碼的基礎上,將其用在ASP.NET MVC程式中。

註冊極驗

到極驗屍官網註冊帳號之後進入後台管理介面,點選新增驗證

MVC使用極驗驗證製作登入驗證碼

新增後我們可以得到ID和KEY

MVC使用極驗驗證製作登入驗證碼

添加後我們可以得到ID和KEY

引入官方的Geetestlib類別

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Net;
using System.IO;
 
namespace PMS.WebApp.Models
{
 /// <summary>
 /// GeetestLib 极验验证C# SDK基本库
 /// </summary>
 public class GeetestLib
 {
  /// <summary>
  /// SDK版本号
  /// </summary>
  public const String version = "3.2.0";
  /// <summary>
  /// SDK开发语言
  /// </summary>
  public const String sdkLang = "csharp";
  /// <summary>
  /// 极验验证API URL
  /// </summary>
  protected const String apiUrl = "http://api.geetest.com";
  /// <summary>
  /// register url
  /// </summary>
  protected const String registerUrl = "/register.php";
  /// <summary>
  /// validate url
  /// </summary>
  protected const String validateUrl = "/validate.php";
  /// <summary>
  /// 极验验证API服务状态Session Key
  /// </summary>
  public const String gtServerStatusSessionKey = "gt_server_status";
  /// <summary>
  /// 极验验证二次验证表单数据 Chllenge
  /// </summary>
  public const String fnGeetestChallenge = "geetest_challenge";
  /// <summary>
  /// 极验验证二次验证表单数据 Validate
  /// </summary>
  public const String fnGeetestValidate = "geetest_validate";
  /// <summary>
  /// 极验验证二次验证表单数据 Seccode
  /// </summary>
  public const String fnGeetestSeccode = "geetest_seccode";
  private String userID = "";
  private String responseStr = "";
  private String captchaID = "";
  private String privateKey = "";
 
  /// <summary>
  /// 验证成功结果字符串
  /// </summary>
  public const int successResult = 1;
  /// <summary>
  /// 证结失败验果字符串
  /// </summary>
  public const int failResult = 0;
  /// <summary>
  /// 判定为机器人结果字符串
  /// </summary>
  public const String forbiddenResult = "forbidden";
 
  /// <summary>
  /// GeetestLib构造函数
  /// </summary>
  /// <param name="publicKey">极验验证公钥</param>
  /// <param name="privateKey">极验验证私钥</param>
  public GeetestLib(String publicKey, String privateKey)
  {
   this.privateKey = privateKey;
   this.captchaID = publicKey;
  }
  private int getRandomNum()
  {
   Random rand =new Random();
   int randRes = rand.Next(100);
   return randRes;
  }
 
  /// <summary>
  /// 验证初始化预处理
  /// </summary>
  /// <returns>初始化结果</returns>
  public Byte preProcess()
  {
   if (this.captchaID == null)
   {
    Console.WriteLine("publicKey is null!");
   }
   else
   {
    String challenge = this.registerChallenge();
    if (challenge.Length == 32)
    {
     this.getSuccessPreProcessRes(challenge);
     return 1;
    }
    else
    {
     this.getFailPreProcessRes();
     Console.WriteLine("Server regist challenge failed!");
    }
   }
 
   return 0;
 
  }
  public Byte preProcess(String userID)
  {
   if (this.captchaID == null)
   {
    Console.WriteLine("publicKey is null!");
   }
   else
   {
    this.userID = userID;
    String challenge = this.registerChallenge();
    if (challenge.Length == 32)
    {
     this.getSuccessPreProcessRes(challenge);
     return 1;
    }
    else
    {
     this.getFailPreProcessRes();
     Console.WriteLine("Server regist challenge failed!");
    }
   }
 
   return 0;
 
  }
  public String getResponseStr()
  {
   return this.responseStr;
  }
  /// <summary>
  /// 预处理失败后的返回格式串
  /// </summary>
  private void getFailPreProcessRes()
  {
   int rand1 = this.getRandomNum();
   int rand2 = this.getRandomNum();
   String md5Str1 = this.md5Encode(rand1 + "");
   String md5Str2 = this.md5Encode(rand2 + "");
   String challenge = md5Str1 + md5Str2.Substring(0, 2);
   this.responseStr = "{" + string.Format(
     "\"success\":{0},\"gt\":\"{1}\",\"challenge\":\"{2}\"", 0,
    this.captchaID, challenge) + "}";
  }
  /// <summary>
  /// 预处理成功后的标准串
  /// </summary>
  private void getSuccessPreProcessRes(String challenge)
  {
   challenge = this.md5Encode(challenge + this.privateKey);
   this.responseStr ="{" + string.Format(
    "\"success\":{0},\"gt\":\"{1}\",\"challenge\":\"{2}\"", 1, 
    this.captchaID, challenge) + "}";
  }
  /// <summary>
  /// failback模式的验证方式
  /// </summary>
  /// <param name="challenge">failback模式下用于与validate一起解码答案, 判断验证是否正确</param>
  /// <param name="validate">failback模式下用于与challenge一起解码答案, 判断验证是否正确</param>
  /// <param name="seccode">failback模式下,其实是个没用的参数</param>
  /// <returns>验证结果</returns>
  public int failbackValidateRequest(String challenge, String validate, String seccode)
  {
   if (!this.requestIsLegal(challenge, validate, seccode)) return GeetestLib.failResult;
   String[] validateStr = validate.Split(&#39;_&#39;);
   String encodeAns = validateStr[0];
   String encodeFullBgImgIndex = validateStr[1];
   String encodeImgGrpIndex = validateStr[2];
   int decodeAns = this.decodeResponse(challenge, encodeAns);
   int decodeFullBgImgIndex = this.decodeResponse(challenge, encodeFullBgImgIndex);
   int decodeImgGrpIndex = this.decodeResponse(challenge, encodeImgGrpIndex);
   int validateResult = this.validateFailImage(decodeAns, decodeFullBgImgIndex, decodeImgGrpIndex);
   return validateResult;
  }
  private int validateFailImage(int ans, int full_bg_index, int img_grp_index)
  {
   const int thread = 3;
   String full_bg_name = this.md5Encode(full_bg_index + "").Substring(0, 10);
   String bg_name = md5Encode(img_grp_index + "").Substring(10, 10);
   String answer_decode = "";
   for (int i = 0;i < 9; i++)
   {
    if (i % 2 == 0) answer_decode += full_bg_name.ElementAt(i);
    else if (i % 2 == 1) answer_decode += bg_name.ElementAt(i);
   }
   String x_decode = answer_decode.Substring(4);
   int x_int = Convert.ToInt32(x_decode, 16);
   int result = x_int % 200;
   if (result < 40) result = 40;
   if (Math.Abs(ans - result) < thread) return GeetestLib.successResult;
   else return GeetestLib.failResult;
  }
  private Boolean requestIsLegal(String challenge, String validate, String seccode)
  {
   if (challenge.Equals(string.Empty) || validate.Equals(string.Empty) || seccode.Equals(string.Empty)) return false;
   return true;
  }
 
  /// <summary>
  /// 向gt-server进行二次验证
  /// </summary>
  /// <param name="challenge">本次验证会话的唯一标识</param>
  /// <param name="validate">拖动完成后server端返回的验证结果标识字符串</param>
  /// <param name="seccode">验证结果的校验码,如果gt-server返回的不与这个值相等则表明验证失败</param>
  /// <returns>二次验证结果</returns>
  public int enhencedValidateRequest(String challenge, String validate, String seccode)
  {
   if (!this.requestIsLegal(challenge, validate, seccode)) return GeetestLib.failResult;
   if (validate.Length > 0 && checkResultByPrivate(challenge, validate))
   {
    String query = "seccode=" + seccode + "&sdk=csharp_" + GeetestLib.version;
    String response = "";
    try
    {
     response = postValidate(query);
    }
    catch (Exception e)
    {
     Console.WriteLine(e);
    }
    if (response.Equals(md5Encode(seccode)))
    {
     return GeetestLib.successResult;
    }
   }
   return GeetestLib.failResult;
  }
  public int enhencedValidateRequest(String challenge, String validate, String seccode, String userID)
  {
   if (!this.requestIsLegal(challenge, validate, seccode)) return GeetestLib.failResult;
   if (validate.Length > 0 && checkResultByPrivate(challenge, validate))
   {
    String query = "seccode=" + seccode + "&user_id=" + userID + "&sdk=csharp_" + GeetestLib.version;
    String response = "";
    try
    {
     response = postValidate(query);
    }
    catch (Exception e)
    {
     Console.WriteLine(e);
    }
    if (response.Equals(md5Encode(seccode)))
    {
     return GeetestLib.successResult;
    }
   }
   return GeetestLib.failResult;
  }
  private String readContentFromGet(String url)
  {
   try
   {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    request.Timeout = 20000;
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    Stream myResponseStream = response.GetResponseStream();
    StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
    String retString = myStreamReader.ReadToEnd();
    myStreamReader.Close();
    myResponseStream.Close();
    return retString;
   }
   catch
   {
    return "";  
   }
 
  }
  private String registerChallenge()
  {
   String url = "";
   if (string.Empty.Equals(this.userID))
   {
    url = string.Format("{0}{1}?gt={2}", GeetestLib.apiUrl, GeetestLib.registerUrl, this.captchaID);
   }
   else
   {
    url = string.Format("{0}{1}?gt={2}&user_id={3}", GeetestLib.apiUrl, GeetestLib.registerUrl, this.captchaID, this.userID);
   }
   string retString = this.readContentFromGet(url);
   return retString;
  }
  private Boolean checkResultByPrivate(String origin, String validate)
  {
   String encodeStr = md5Encode(privateKey + "geetest" + origin);
   return validate.Equals(encodeStr);
  }
  private String postValidate(String data)
  {
   String url = string.Format("{0}{1}", GeetestLib.apiUrl, GeetestLib.validateUrl);
   HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
   request.Method = "POST";
   request.ContentType = "application/x-www-form-urlencoded";
   request.ContentLength = Encoding.UTF8.GetByteCount(data);
   // 发送数据
   Stream myRequestStream = request.GetRequestStream();
   byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(data);
   myRequestStream.Write(requestBytes, 0, requestBytes.Length);
   myRequestStream.Close();
 
   HttpWebResponse response = (HttpWebResponse)request.GetResponse();
   // 读取返回信息
   Stream myResponseStream = response.GetResponseStream();
   StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
   string retString = myStreamReader.ReadToEnd();
   myStreamReader.Close();
   myResponseStream.Close();
 
   return retString;
 
  }
  private int decodeRandBase(String challenge)
  {
   String baseStr = challenge.Substring(32, 2);
   List<int> tempList = new List<int>();
   for(int i = 0; i < baseStr.Length; i++)
   {
    int tempAscii = (int)baseStr[i];
    tempList.Add((tempAscii > 57) ? (tempAscii - 87)
     : (tempAscii - 48));
   }
   int result = tempList.ElementAt(0) * 36 + tempList.ElementAt(1);
   return result;
  }
  private int decodeResponse(String challenge, String str)
  {
   if (str.Length>100) return 0;
   int[] shuzi = new int[] { 1, 2, 5, 10, 50};
   String chongfu = "";
   Hashtable key = new Hashtable();
   int count = 0;
   for (int i=0;i<challenge.Length;i++)
   {
    String item = challenge.ElementAt(i) + "";
    if (chongfu.Contains(item)) continue;
    else
    {
     int value = shuzi[count % 5];
     chongfu += item;
     count++;
     key.Add(item, value);
    }
   }
   int res = 0;
   for (int i = 0; i < str.Length; i++) res += (int)key[str[i]+""];
   res = res - this.decodeRandBase(challenge);
   return res;
  }
  private String md5Encode(String plainText)
  {
   MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
   string t2 = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(plainText)));
   t2 = t2.Replace("-", "");
   t2 = t2.ToLower();
   return t2;
  }
 
 }
}
登入後複製

2. 取得驗證碼

引入Jquery庫


新增用於放置驗證碼的div(需要放到form表單中)


新增JS程式碼用於取得驗證碼

proreee

estcessGeeTTing方法中我們非同步請求的位址「/Login/GeekTest」就是取得驗證碼是後台需要執行的方法

<script>
 window.addEventListener(&#39;load&#39;, processGeeTest);
 
 function processGeeTest() {
  $.ajax({
   // 获取id,challenge,success(是否启用failback)
   url: "/Login/GeekTest",
   type: "get",
   dataType: "json", // 使用jsonp格式
   success: function (data) {
    // 使用initGeetest接口
    // 参数1:配置参数,与创建Geetest实例时接受的参数一致
    // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件
    initGeetest({
     gt: data.gt,
     challenge: data.challenge,
     product: "float", // 产品形式
     offline: !data.success
    },
     handler);
   }
  });
 }
 
 var handler = function (captchaObj) {
  // 将验证码加到id为captcha的元素里
  captchaObj.appendTo("#geetest-container");
 
  captchaObj.onSuccess = function (e) {
   console.log(e);
  }
 
 };
</script>
登入後複製

3. 校驗驗證碼

注意,當提交form表單時,會將三個和極驗有關的參數傳到後台方法(geetest_challenge、geetest_validate、geetest_seccode),若驗證碼未驗證成功,參數為空值。

後台驗證方法為:

public ActionResult GeekTest()
{
 return Content(GetCaptcha(),"application/json");
}
 
private string GetCaptcha()
{
 var geetest = new GeetestLib("3594e0d834df77cedc7351a02b5b06a4", "b961c8081ce88af7e32a3f45d00dff84");
 var gtServerStatus = geetest.preProcess();
 Session[GeetestLib.gtServerStatusSessionKey] = gtServerStatus;
 return geetest.getResponseStr();
}
登入後複製

我們可以在表單中判斷驗證碼是否成功校驗:

private bool CheckGeeTestResult()
{
 var geetest = new GeetestLib("3594e0d834df77cedc7351a02b5b06a4", "b961c8081ce88af7e32a3f45d00dff84 ");
 var gtServerStatusCode = (byte)Session[GeetestLib.gtServerStatusSessionKey];
 var userId = (string)Session["userID"];
 
 var challenge = Request.Form.Get(GeetestLib.fnGeetestChallenge);
 var validate = Request.Form.Get(GeetestLib.fnGeetestValidate);
 var seccode = Request.Form.Get(GeetestLib.fnGeetestSeccode);
 var result = gtServerStatusCode == 1 ? geetest.enhencedValidateRequest(challenge, validate, seccode, userId) : geetest.failbackValidateRequest(challenge, validate, seccode);
 return result == 1;
}
登入後複製

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支PHP網中文。

更多MVC使用極驗驗證製作登入驗證碼相關文章請關注PHP中文網!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1662
14
CakePHP 教程
1418
52
Laravel 教程
1311
25
PHP教程
1261
29
C# 教程
1234
24
C#.NET面試問題和答案:提高您的專業知識 C#.NET面試問題和答案:提高您的專業知識 Apr 07, 2025 am 12:01 AM

C#.NET面試問題和答案包括基礎知識、核心概念和高級用法。 1)基礎知識:C#是微軟開發的面向對象語言,主要用於.NET框架。 2)核心概念:委託和事件允許動態綁定方法,LINQ提供強大查詢功能。 3)高級用法:異步編程提高響應性,表達式樹用於動態代碼構建。

C#.NET:探索核心概念和編程基礎知識 C#.NET:探索核心概念和編程基礎知識 Apr 10, 2025 am 09:32 AM

C#是一種現代、面向對象的編程語言,由微軟開發並作為.NET框架的一部分。 1.C#支持面向對象編程(OOP),包括封裝、繼承和多態。 2.C#中的異步編程通過async和await關鍵字實現,提高應用的響應性。 3.使用LINQ可以簡潔地處理數據集合。 4.常見錯誤包括空引用異常和索引超出範圍異常,調試技巧包括使用調試器和異常處理。 5.性能優化包括使用StringBuilder和避免不必要的裝箱和拆箱。

測試C#.NET應用程序:單元,集成和端到端測試 測試C#.NET應用程序:單元,集成和端到端測試 Apr 09, 2025 am 12:04 AM

C#.NET應用的測試策略包括單元測試、集成測試和端到端測試。 1.單元測試確保代碼的最小單元獨立工作,使用MSTest、NUnit或xUnit框架。 2.集成測試驗證多個單元組合的功能,常用模擬數據和外部服務。 3.端到端測試模擬用戶完整操作流程,通常使用Selenium進行自動化測試。

c#.net的持續相關性:查看當前用法 c#.net的持續相關性:查看當前用法 Apr 16, 2025 am 12:07 AM

C#.NET依然重要,因為它提供了強大的工具和庫,支持多種應用開發。 1)C#結合.NET框架,使開發高效便捷。 2)C#的類型安全和垃圾回收機制增強了其優勢。 3).NET提供跨平台運行環境和豐富的API,提升了開發靈活性。

從網絡到桌面:C#.NET的多功能性 從網絡到桌面:C#.NET的多功能性 Apr 15, 2025 am 12:07 AM

C#.NETisversatileforbothwebanddesktopdevelopment.1)Forweb,useASP.NETfordynamicapplications.2)Fordesktop,employWindowsFormsorWPFforrichinterfaces.3)UseXamarinforcross-platformdevelopment,enablingcodesharingacrossWindows,macOS,Linux,andmobiledevices.

高級C#.NET教程:ACE您的下一次高級開發人員面試 高級C#.NET教程:ACE您的下一次高級開發人員面試 Apr 08, 2025 am 12:06 AM

C#高級開發者面試需要掌握異步編程、LINQ、.NET框架內部工作原理等核心知識。 1.異步編程通過async和await簡化操作,提升應用響應性。 2.LINQ以SQL風格操作數據,需注意性能。 3..NET框架的CLR管理內存,垃圾回收需謹慎使用。

c#.net適合您嗎?評估其適用性 c#.net適合您嗎?評估其適用性 Apr 13, 2025 am 12:03 AM

c#.netissutableforenterprise-levelapplications withemofrosoftecosystemdueToItsStrongTyping,richlibraries,androbustperraries,androbustperformance.however,itmaynotbeidealfoross-platement forment forment forment forvepentment offependment dovelopment toveloperment toveloperment whenrawspeedsportor whenrawspeedseedpolitical politionalitable,

C#.NET與未來:適應新技術 C#.NET與未來:適應新技術 Apr 14, 2025 am 12:06 AM

C#和.NET通過不斷的更新和優化,適應了新興技術的需求。 1)C#9.0和.NET5引入了記錄類型和性能優化。 2).NETCore增強了雲原生和容器化支持。 3)ASP.NETCore與現代Web技術集成。 4)ML.NET支持機器學習和人工智能。 5)異步編程和最佳實踐提升了性能。

See all articles