MVC5使用Geetest極驗驗證碼示例
阿新 • • 發佈:2019-02-06
Models資料夾實體類
LoginInfo.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace MvcDemo.Models.Geetest
{
public class LoginInfo
{
[Required(ErrorMessage="使用者名稱不能為空")]
public string UserName { get ; set; }
[Required(ErrorMessage = "密碼不能為空")]
public string Password { get; set; }
}
}
GeetestConfig.cs
using System;
namespace MvcDemo.Models.Geetest
{
public class GeetestConfig
{
/// <summary>
/// 驗證ID
/// </summary>
public const string PublicKey = "6ef7c61ac11117de6996efa65be1c27e";
/// <summary>
/// 驗證Key
/// </summary>
public const string PrivateKey = "62add4e324dd9e9ef2ea35192f8d9181";
}
}
GeetestLib.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
namespace MvcDemo.Models.Geetest
{
/// <summary>
/// GeetestLib 極驗驗證C# SDK基本庫
/// </summary>
public class GeetestLib
{
/// <summary>
/// SDK版本號
/// </summary>
public const string Version = "3.1.1";
/// <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";
/// <summary>
/// 驗證成功結果字串
/// </summary>
public const int SuccessResult = 1;
/// <summary>
/// 證結失敗驗果字串
/// </summary>
public const int FailResult = 0;
/// <summary>
/// 判定為機器人結果字串
/// </summary>
public const string ForbiddenResult = "forbidden";
private readonly string captchaID = "";
private readonly string privateKey = "";
private string responseStr = "";
/// <summary>
/// GeetestLib建構函式
/// </summary>
/// <param name="publicKey">極驗驗證公鑰</param>
/// <param name="privateKey">極驗驗證私鑰</param>
public GeetestLib(string publicKey, string privateKey)
{
this.privateKey = privateKey;
captchaID = publicKey;
}
private int GetRandomNum()
{
var rand = new Random();
var randRes = rand.Next(100);
return randRes;
}
/// <summary>
/// 驗證初始化預處理
/// </summary>
/// <returns>初始化結果</returns>
public byte PreProcess()
{
if (captchaID == null)
{
Console.WriteLine("publicKey is null!");
}
else
{
var challenge = RegisterChallenge();
if (challenge.Length == 32)
{
GetSuccessPreProcessRes(challenge);
return 1;
}
GetFailPreProcessRes();
Console.WriteLine("Server regist challenge failed!");
}
return 0;
}
public string GetResponseStr()
{
return responseStr;
}
/// <summary>
/// 預處理失敗後的返回格式串
/// </summary>
private void GetFailPreProcessRes()
{
var rand1 = GetRandomNum();
var rand2 = GetRandomNum();
var md5Str1 = md5Encode(rand1 + "");
var md5Str2 = md5Encode(rand2 + "");
var challenge = md5Str1 + md5Str2.Substring(0, 2);
responseStr = "{" + string.Format("\"success\":{0},\"gt\":\"{1}\",\"challenge\":\"{2}\"", 0, captchaID, challenge) + "}";
}
/// <summary>
/// 預處理成功後的標準串
/// </summary>
private void GetSuccessPreProcessRes(string challenge)
{
challenge = md5Encode(challenge + privateKey);
responseStr = "{" + string.Format("\"success\":{0},\"gt\":\"{1}\",\"challenge\":\"{2}\"", 1, 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 (!RequestIsLegal(challenge, validate, seccode)) return FailResult;
var validateStr = validate.Split('_');
var encodeAns = validateStr[0];
var encodeFullBgImgIndex = validateStr[1];
var encodeImgGrpIndex = validateStr[2];
var decodeAns = DecodeResponse(challenge, encodeAns);
var decodeFullBgImgIndex = DecodeResponse(challenge, encodeFullBgImgIndex);
var decodeImgGrpIndex = DecodeResponse(challenge, encodeImgGrpIndex);
var validateResult = ValidateFailImage(decodeAns, decodeFullBgImgIndex, decodeImgGrpIndex);
return validateResult;
}
private int ValidateFailImage(int ans, int full_bg_index, int img_grp_index)
{
const int thread = 3;
var fullBgName = md5Encode(full_bg_index + "").Substring(0, 10);
var bgName = md5Encode(img_grp_index + "").Substring(10, 10);
var answerDecode = "";
for (var i = 0; i < 9; i++)
{
switch (i % 2)
{
case 0:
answerDecode += fullBgName.ElementAt(i);
break;
case 1:
answerDecode += bgName.ElementAt(i);
break;
}
}
var xDecode = answerDecode.Substring(4);
var xInt = Convert.ToInt32(xDecode, 16);
var result = xInt % 200;
if (result < 40) result = 40;
return Math.Abs(ans - result) < thread ? SuccessResult : FailResult;
}
private bool RequestIsLegal(string challenge, string validate, string seccode)
{
return !challenge.Equals(string.Empty) && !validate.Equals(string.Empty) && !seccode.Equals(string.Empty);
}
/// <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 (!RequestIsLegal(challenge, validate, seccode)) return FailResult;
if (validate.Length <= 0 || !CheckResultByPrivate(challenge, validate)) return FailResult;
var query = "seccode=" + seccode + "&sdk=csharp_" + Version;
var response = "";
try
{
response = PostValidate(query);
}
catch (Exception e)
{
Console.WriteLine(e);
}
return response.Equals(md5Encode(seccode)) ? SuccessResult : FailResult;
}
private string ReadContentFromGet(string url)
{
try
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = 20000;
var response = (HttpWebResponse)request.GetResponse();
var myResponseStream = response.GetResponseStream();
var myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
var retstring = myStreamReader.ReadToEnd();
myStreamReader.Close();
myResponseStream.Close();
return retstring;
}
catch
{
return "";
}
}
private string RegisterChallenge()
{
var url = string.Format("{0}{1}?gt={2}", ApiUrl, RegisterUrl, captchaID);
var retstring = ReadContentFromGet(url);
return retstring;
}
private bool CheckResultByPrivate(string origin, string validate)
{
var encodeStr = md5Encode(privateKey + "geetest" + origin);
return validate.Equals(encodeStr);
}
private string PostValidate(string data)
{
var url = string.Format("{0}{1}", ApiUrl, ValidateUrl);
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = Encoding.UTF8.GetByteCount(data);
// 傳送資料
var myRequestStream = request.GetRequestStream();
var requestBytes = Encoding.ASCII.GetBytes(data);
myRequestStream.Write(requestBytes, 0, requestBytes.Length);
myRequestStream.Close();
var response = (HttpWebResponse)request.GetResponse();
// 讀取返回資訊
var myResponseStream = response.GetResponseStream();
var myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
var retstring = myStreamReader.ReadToEnd();
myStreamReader.Close();
myResponseStream.Close();
return retstring;
}
private int DecodeRandBase(string challenge)
{
var baseStr = challenge.Substring(32, 2);
var tempList = new List<int>();
for (var i = 0; i < baseStr.Length; i++)
{
var tempAscii = (int)baseStr[i];
tempList.Add((tempAscii > 57)
? (tempAscii - 87)
: (tempAscii - 48));
}
var result = tempList.ElementAt(0) * 36 + tempList.ElementAt(1);
return result;
}
private int DecodeResponse(string challenge, string str)
{
if (str.Length > 100) return 0;
var shuzi = new[] { 1, 2, 5, 10, 50 };
var chongfu = "";
var key = new Hashtable();
var count = 0;
for (var i = 0; i < challenge.Length; i++)
{
var item = challenge.ElementAt(i) + "";
if (chongfu.Contains(item)) continue;
var value = shuzi[count % 5];
chongfu += item;
count++;
key.Add(item, value);
}
var res = 0;
for (var i = 0; i < str.Length; i++) res += (int)key[str[i] + ""];
res = res - DecodeRandBase(challenge);
return res;
}
private string md5Encode(string plainText)
{
var md5 = new MD5CryptoServiceProvider();
var t2 = BitConverter.ToString(md5.ComputeHash(Encoding.Default.GetBytes(plainText)));
t2 = t2.Replace("-", "");
t2 = t2.ToLower();
return t2;
}
}
}
控制器層
GeetestController.cs
using System.Web.Mvc;
using MvcDemo.Models.Geetest;
namespace MvcDemo.Controllers
{
public class GeetestController : Controller
{
public ActionResult Geetest()
{
var loginInfo = new LoginInfo();
return View(loginInfo);
}
[HttpPost]
public ActionResult Geetest(LoginInfo loginInfo)
{
if (!ModelState.IsValid)
{
ViewData["message"] = "請填寫完整資料";
return View(loginInfo);
}
if (!IsVerifyCaptcha())
{
ViewData["message"] = "驗證碼錯誤";
return View(loginInfo);
}
if (loginInfo.UserName == "admin" && loginInfo.Password == "123123")
{
ViewData["message"] = "登入成功";
return View(loginInfo);
}
ViewData["message"] = "使用者名稱密碼錯誤";
return View(loginInfo);
}
/// <summary>
/// 驗證碼是否正確
/// </summary>
/// <returns></returns>
public bool IsVerifyCaptcha()
{
var geetest = new GeetestLib(GeetestConfig.PublicKey, GeetestConfig.PrivateKey);
var gtServerStatusCode = (byte) Session[GeetestLib.GtServerStatusSessionKey];
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)
: geetest.FailbackValidateRequest(challenge, validate, seccode);
return result == 1;
}
/// <summary>
/// 獲取驗證碼
/// </summary>
/// <returns></returns>
public ContentResult GetCaptcha()
{
var geetest = new GeetestLib(GeetestConfig.PublicKey, GeetestConfig.PrivateKey);
var gtServerStatus = geetest.PreProcess();
Session[GeetestLib.GtServerStatusSessionKey] = gtServerStatus;
return Content(geetest.GetResponseStr());
}
}
}
Geetest.cshtml
@model MvcDemo.Models.Geetest.LoginInfo
@{
ViewBag.Title = "Geetest";
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="http://static.geetest.com/static/tools/gt.js"></script>
}
<form class="form-signin" action="@Url.Action("Geetest", "Geetest")" method="post">
<h2 class="form-signin-heading">Please sign in</h2>
@if (ViewData["message"] != null)
{
<div class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
@ViewData["message"].ToString()
</div>
}
<label>使用者名稱</label>
@Html.TextBoxFor(n => n.UserName, new { placeholder = "使用者名稱", @class = "form-control", autofocus = "" })
@Html.ValidationMessageFor(n => n.UserName)
<br />
<label>密碼</label>
@Html.PasswordFor(n => n.Password, new { placeholder = "密碼", @class = "form-control" })
@Html.ValidationMessageFor(n => n.Password)
<br />
<label for="captcha">驗證碼</label>
<div id="captcha"></div>
<br />
<button type="submit" class="btn btn-lg btn-primary">提交</button>
</form>
<script>
$.ajax({
// 獲取id,challenge,success(是否啟用failback)
url: '@Url.Action("GetCaptcha", "Geetest")',
type: "get",
dataType: "json", // 使用jsonp格式
success: function (data) {
// 使用initGeetest介面
// 引數1:配置引數,與建立Geetest例項時接受的引數一致
// 引數2:回撥,回撥的第一個引數驗證碼物件,之後可以使用它做appendTo之類的事件
window.initGeetest({
gt: data.gt,
challenge: data.challenge,
//product: "embed", // 產品形式
offline: !data.success
}, function (captchaObj) {
// 將驗證碼加到id為captcha的元素裡
captchaObj.appendTo("#captcha");
});
}
});
</script>