【Asp.net】使用 reCAPTCHA 作为网站验证码

(本文暂不讨论reCAPTCHA Android的使用方式,因为我也没用过XD)

1.申请reCAPTCHA API keys

想要使用reCAPTCHA,首先需要在reCAPTCHA控制台申请服务,并选择需要使用的API版本

其中,reCAPTCHA V2版需要用户选择一个复选框,Google将会在屏幕上呈现一些判断题,以验证用户是机器人还是人类

例如:

而reCAPTCHA Invisible则不需要用户选择复选框,可以直接集成在网页的提交按钮中,由Google根据用户的流量自动判断访问页面的是机器人还是人类

当Google发现可疑流量时,才会弹出与reCAPTCHA v2一样的验证码,此时,网页右下角会显示如下标志:

2.设置API

reCAPTCHA只会允许后台内设置的域名使用验证服务,否则会出现如下错误提示:

为了方便本地调试,建议在域名列表(Domains)中添加localhost与127.0.0.1

例如:

点击“Save changees”按钮,更新设置。

3.在页面中添加相应的控件

本部分以reCAPTCHA v2为例,整个验证的流程为:

(1)用户点击验证,前端页面获取到一个g-recaptcha-response值;

(2)将这个值发送到后端;

(3)后端再将Secret key、g-recaptcha-response、用户客户端ip(可选)一起发送到Google服务器;

(4)Google向后端返回是否验证成功;

(5)接入验证流程……

找到页面内的“Adding reCAPTCHA to your site”处,点击“Keys”,获取密钥

Site key需要放在前端页面中,而Secret key则需要放在后端,不能泄露

在网页头部添加如下js文件:

<script src='https://www.google.com/recaptcha/api.js'></script>

因为Google reCAPTCHA所在域名被墙,如果服务需要在国内访问,请替换为如下地址:

<script src="https://www.recaptcha.net/recaptcha/api.js"></script>

然后在页面中添加相应的控件:

<div class="g-recaptcha" data-sitekey="6LdSpUAUAAAAACpgcW_WPDqte35A0OLqwDm6KdwV" data-callback="flushStatus"></div>

其中,“data-sitekey”的值为申请到的Site key,“data-callback”为用户完成验证后调用的回调函数。

这样,我们就搭建出了一个基本的页面:

(注意!ajax.js仅为个人方便而调用的ajax轮子,可以替换为js原生方法或jQuery ajax)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>reCAPTCHA v2 Demo</title>
    <script type="text/javascript" src="https://www.recaptcha.net/recaptcha/api.js"></script>
    <script type="text/javascript" src="js/ajax.js"></script>
</head>
<body>
    <div class="g-recaptcha" data-sitekey="6LdSpUAUAAAAACpgcW_WPDqte35A0OLqwDm6KdwV" data-callback="flushStatus"></div>
    <input type="button" value="submit" id="btn-submit" />
    <br />
    <p style="display:inline;color:red">验证状态: </p><span id="sp1"></span>
    <br />
    <p style="display:inline;color:red">回调结果: </p><span id="sp2"></span>
    <br />
    <p style="display:inline;color:red">取值结果: </p><span id="sp3"></span>
    <br />
    <p style="display:inline;color:red">最终状态: </p><span id="sp4"></span>
</body>
</html>

 

(1)获取g-recaptcha-response

有两种方式可以获取到这个值:

1. 使用grecaptcha.getResponse()函数
2. 在data-callback中添加一个回调函数

比如:

        function flushStatus(data) {
            document.getElementById("sp2").innerText = grecaptcha.getResponse();//直接取得g-recaptcha-response
            document.getElementById("sp3").innerText = data;//利用回调函数取得
        }

此处可以看出,他们获取的结果是相同的。

(2)将这个值发送到后端

        document.getElementById("btn-submit").onclick = function () {
            let verifyCode = grecaptcha.getResponse();
            if (!verifyCode) {//如果g-recaptcha-response不为空
                document.getElementById("sp1").innerText = "没有验证";
            } else {
                document.getElementById("sp1").innerText = "已经验证";

                let ajax = new Ajax();
                ajax.send({
                    method: "POST",
                    url: "/verify.ashx",
                    resType: "json",
                    data: {
                        code: verifyCode//将g-recaptcha-response值传到后端
                    },
                    success: function (data) {
                        document.getElementById("sp4").innerText = JSON.stringify(data);
                    }
                });
            }
        }

(3)后端验证

本文编写的是Asp.net一般处理程序(.ashx),并使用了Newtonsoft.Json框架来解析json数据,仅供参考

需要特别注意的是,向Google服务器提交数据时,虽然提交方式是POST,但必须像GET请求那样使用url参数(此处存疑?求dalao解答),否则会返回缺少参数的错误

(被这个坑了好久,md我的心好痛)

        public class RequestData {
            public string secret;
            public string response;
        }

        public void ProcessRequest(HttpContext context)
        {
            RequestData requestData = new RequestData();
            requestData.secret = "6LdSpUAUAAAAAKkS_4bojjF16mhzoPXXIsyZdN8j";//站点secret key
            requestData.response = context.Request.Form["code"];//客户端返回结果
            if (requestData.response != "")
            {
                string postUrl = "https://recaptcha.net/recaptcha/api/siteverify?secret=" + requestData.secret + "&response=" + requestData.response;
                string postResult = HttpPost(postUrl);//发送
                Debug.WriteLine(postResult);
                JObject jObject = JsonConvert.DeserializeObject(postResult) as JObject;//反序列化验证结果
                if (jObject["success"].ToString().ToLower()=="true")
                {
                    context.Response.ContentType = "application/json";
                    context.Response.Write("{\"status\":\"true\"}");
                }
                else
                {
                    context.Response.ContentType = "application/json";
                    context.Response.Write("{\"status\":\"false\"}");
                }
            }
            else
            {
                context.Response.ContentType = "application/json";
                context.Response.Write("{\"status\":\"false\"}");
            }
        }

        public static string HttpPost(string url, string postDataStr = "")//模拟POST请求
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "POST";
            request.ContentType = "application/json";
            request.ContentLength = postDataStr.Length;
            StreamWriter writer = new StreamWriter(request.GetRequestStream(), Encoding.ASCII);
            writer.Write(postDataStr);
            writer.Flush();
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            string encoding = response.ContentEncoding;
            if (encoding == null || encoding.Length < 1)
            {
                encoding = "UTF-8";
            }
            StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(encoding));
            string retString = reader.ReadToEnd();
            return retString;
        }

(4)Google返回的结果

这是一个验证成功的例子:

{
  "success": true,
  "challenge_ts": "2018-02-10T10:31:22Z",
  "hostname": "localhost"
}

这是一个验证失败的例子,失败的原因是缺少参数:

{
  "success": false,
  "error-codes": [
    "missing-input-response",
    "missing-input-secret"
  ]
}

以下是Google官方对于error-codes的说明:

missing-input-secret secret参数丢失
invalid-input-secret secret参数无效或格式错误
missing-input-response response参数丢失
invalid-input-response response参数无效或格式错误
bad-request 请求无效或格式错误

(5)接入验证流程

单独地使用reCAPTCHA并没有什么用处,如果最终的判断仍在前端,那么该验证还是可以被篡改的。

一个建议的使用方式是,将g-recaptcha-response值参杂在提交的表单中。

5.总结

本文编写的reCAPTCHA v2完整例子,以及并未列出的reCAPTCHA Invisible例子,均已上传。

reCAPTCHA v2:https://github.com/KatyushaScarlet/reCAPTCHA-v2-Demo

reCAPTCHA Invisible:https://github.com/KatyushaScarlet/reCAPTCHA-Invisible-Demo

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注