【问题标题】:reCAPTCHA custom form parameter namesreCAPTCHA 自定义表单参数名称
【发布时间】:2012-05-28 22:16:24
【问题描述】:

是否可以自定义 reCAPTCHA 表单参数(recaptcha_challenge_fieldrecaptcha_response_field)以便以不同方式调用它们?

基本上我希望表单参数 recaptcha_challenge_field 被称为 captchaIdrecaptcha_response_field 被称为 captchaUserResponse

我希望它们重命名,以便我可以抽象验证码实现...当请求到达时

POST /mysite/userSignup

我不想为验证码实现(reCaptcha 或将来的其他东西)而烦恼 - 为正确的验证码实现提取正确的参数,我想统一这些参数名称。

现在我的请求如下所示

POST /mysite/userSignup HTTP/1.1
Host: localhost:80
Connection: keep-alive
Content-Length: 416
Cache-Control: max-age=0
Origin: http://localhost:80
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46 Safari/536.5
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://localhost:8080/mysite/signup/form.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,hr;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

email:testUser%40gmail.com
username:testUser
password:test
password2:test
forename:Test
surname:User
recaptcha_challenge_field:<google generated challange>
recaptcha_response_field:<user typed captcha answer>
submit:Submit

但我希望它看起来像这样:

POST /mysite/userSignup HTTP/1.1
Host: localhost:80
Connection: keep-alive
Content-Length: 416
Cache-Control: max-age=0
Origin: http://localhost:80
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46 Safari/536.5
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://localhost:8080/mysite/signup/form.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,hr;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

email:testUser%40gmail.com
username:testUser
password:test
password2:test
forename:Test
surname:User
captchaId:<google generated challange>
captchaUserResponse:<user typed captcha answer>
submit:Submit

一种优雅的方法是像这样指定这些表单参数名称:

<script>
   var RecaptchaOptions = {
      recaptcha_challenge_field_formparam_name : 'captchaId',
      recaptcha_response_field_formparam_name: 'captchaUserResponse'
   };
</script>

如果这不可行,您建议什么解决方法?

【问题讨论】:

标签: javascript forms http captcha recaptcha


【解决方案1】:

这应该是不可能的。 AFAIU 可能的解决方案是添加新的 DOM 元素,这些元素在表单提交之前复制原始元素的值,例如:

<!-- the HTML part -->
<input type="hidden" id="captchaId">
<input type="hidden" id="captchaUserResponse">

// the JavaScript (jQuery) part
$('form').submit(function(){
  $('input#captchaId').
    val($('input#recaptcha_response_field').val());
  $('input#captchaUserResponse').
    val($('input#recaptcha_challenge_field').val());
  return true;
})

【讨论】:

  • 你为什么认为他在使用 jQuery?
  • @Truth:我不知道;简单地说 jQuery 使解释更容易(至少对我来说)。
【解决方案2】:

不需要额外的字段。您所要做的就是用 id 标识表单元素并在提交之前更改它们的相对名称。

假设你有以下表格:

<div id="captcha" class="item recaptcha">
...
<textarea id=custom1 name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
<input id=custom2 name="recaptcha_response_field" value="manual_challenge" type="hidden" />
<input type="submit" name="submit" value="Submit">
</div>

您可以使用更改名称

document.captcha.custom1.name='captchaId'
document.captcha.custom2.name='captchaUserResponse'

可以使用 onclick 事件直接或间接(调用另一个方法)将这些更改包含在提交按钮中。

因此,对于直接更改,您的提交将是:

<input type="submit" name="submit" value="Submit" onclick="document.captcha.custom1.name='captchaId';document.captcha.custom2.name='captchaUserResponse';return true">

【讨论】:

  • 没关系,但由于我没有为单个站点构建单个注册表单,因此它必须尽可能易于嵌入,无需额外的客户干预。例如:
    myserver/myApi/signup">
  • recaptcha 有自己的实例规则,无法更改或适应您的抽象概念。因此,您必须处理 Vidul 提出的客户端干预或处理我的面向服务器的建议(html 是在服务器上生成的,不需要外部 JS 库)。因此,对于 recaptcha,您的服务器代码可能有一个接受表单操作作为参数并生成相关 html 代码的例程。对于其他验证码实现,更改例程以接受该实现作为参数并使用 switch 或 if 语句生成 html。
  • 这些是您回答的问题:(1)您正在使用ID进行引用,原始表单可能没有这样的属性。 (2) 您正在使用不适合此事件的 onclick 事件,因为可以通过 TAB 和 ENETER 键组合提交表单。
【解决方案3】:

由于 OP 尚未接受任何答案,因此我提供了这个答案。这正是 OP 在这篇文章的 cmets 中寻找的东西:one of the answers

创建一个 PHP 脚本并将其放在某个地方。我假设创建页面的 URI 是DESIRED_URI。将此代码放入其中:

<?php
    header("Content-type: text/javascript");
    /*
    * These are the script parameters
    * You can change them by including desired ones in script's URL (as GET params)
    * challenge_field_original: original name of captcha challenge field
    * response_field_original: original name of captcha response field
    * to_challenge_field: the name of the challenge field you want
    * to_response_field: the name of the response field you want
    */
    $params = array(
        /*
        * Defining the defaults of the parameters
        */
        "challenge_field_original" => "recaptcha_challenge_field",
        "response_field_original" => "recaptcha_response_field",
        "to_challenge_field" => "captchaId",
        "to_response_field" => "captchaUserResponse",
    );
    /*
    * Checking for passed GET params to re-fill the parameters with non-default ones (if any)
    */
    if(isset($_GET['challenge']) && $_GET['challenge'] != "") {
        $params["to_challenge_field"] = $_GET['challenge'];
    }
    if(isset($_GET['response']) && $_GET['response'] != "") {
        $params["to_response_field"] = $_GET['response'];
    }
    if(isset($_GET['challenge_original']) && $_GET['challenge_original'] != "") {
        $params["challenge_field_original"] = $_GET['challenge_original'];
    }
    if(isset($_GET['response_original']) && $_GET['response_original'] != "") {
        $params["response_field_original"] = $_GET['response_original'];
    }
?>
/*
* I'm going to find the index of this script tag in the whole document
* So I can find its previous sibling node (the captcha form node)
*/
var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];

//now we have the index, good, I'm going to find its previous sibling node
var targetFormTag = (function(element) {
    if(typeof(element.previousElementSibling) == "undefined") {
        var p = element;
        do p = p.previousSibling;
        while (p && p.nodeType != 1);
        return p;
    } else {
        return element.previousElementSibling;
    }
})(thisScriptTag);

//Changing names...
(function(targetForm) {
    var a = targetForm.getElementsByTagName('input');
    var attr = '';
    var toAttr = '';
    for (var i in a) {
        try {
            attr = a[i].getAttribute('name')
        } catch(e) {
            continue;
        }
        switch(attr) {
            case '<?php echo $params["challenge_field_original"]; ?>':
            {toAttr='<?php echo $params["to_challenge_field"]; ?>';break;}
            case '<?php echo $params["response_field_original"]; ?>':
            {toAttr='<?php echo $params["to_response_field"]; ?>';break;}
        }
        a[i].setAttribute('name',toAttr);
    }
})(targetFormTag)

用法:

就像在表单后面包含脚本一样简单(!这非常重要!)。像这样:

<form><!--form elements--></form>
<script type="text/javascript" src="DESIRED_URI"></script>

您可以在一段时间后更改字段的名称,为此,请阅读代码文档。

旁注:

  • 该脚本可笑地跨浏览器(IE5+、Chrome、FF、Android)
  • 您可以将其用于任何验证码脚本,只需在脚本的src 中提供原始输入的名称和该输入字段的所需名称。
  • 脚本尝试更改字段,失败时不会引发任何错误

【讨论】:

  • 我已经在服务器端做了参数名映射。我想 reCAPTCHA 不支持自定义表单参数名称 - 所以服务器端解决方案是最干净的。
  • @davorp 我真的很想帮忙,我只知道这些答案,祝你一切顺利;D
【解决方案4】:

给你,我和你的情况一样,我就是这样做到的:

短版:
在这里,将此属性添加到您的表单中,这就是您所需要的:

EDIT2 - 添加纯 Javascript 代码

纯 JavaScript 方法
在 IE8、FF12 和 Chrome 上测试:

onsubmit="javascript:(function(p){var a=p.getElementsByTagName('input');var attr='';var toAttr='';for (var i in a){try {attr=a[i].getAttribute('name')} catch(e) {continue;}switch(attr){case 'recaptcha_challenge_field':{toAttr='captchaId';break;}case 'recaptcha_response_field':{toAttr='captchaUserResponse';break;}}a[i].setAttribute('name',toAttr);}p.submit();})(this)"

jQuery 方法:

onsubmit="javascript:(function(p){var f=$(p);$('input[name=recaptcha_challenge_field]',f).attr('name','captchaId');$('input[name=recaptcha_response_field]',f).attr('name','captchaUserResponse');f.submit();})(this)"

E.G:

<form method="post" action="verify.php" onsubmit="javascript:(function(p){var f=$(p);$('input[name=recaptcha_challenge_field]',f).attr('name','captchaId');$('input[name=recaptcha_response_field]',f).attr('name','captchaUserResponse');f.submit();})(this)">
    <?php
    require_once('recaptchalib.php');
    $publickey = "your_public_key"; // you got this from the signup page
    echo recaptcha_get_html($publickey);
    ?>
    <input type="submit" />
</form>

加长版:
这是我正在做的事情:

1- 这是负责替换输入名称的实际 javascript 函数,这里没什么特别的:

function(param){
    var form = $(param);
    $('input[name=recaptcha_challenge_field]',form).attr('name','captchaId');
    $('input[name=recaptcha_response_field]',form).attr('name','captchaUserResponse');
    form.submit();
}

2-我将把提到的函数设为匿名函数,这样我就可以通过内联 HTML 事件(onsubmit 事件)调用它:

(function(param){...function code...})(this)
  • 注意到(this)?在我们的匿名函数结束时?经过 这样,我可以将表单的句柄传递给函数。

3- 由于您需要尽可能少的客户端修改(或您所说的干预?),我将从 HTML 内联事件处理程序调用此匿名函数。在我们的例子中,最好的处理程序是onsubmit

老实说,我自己很喜欢这个解决方案,希望它对你也有用:D

EDIT1:

顺便说一句,我没有注意到你的cmets,如果你想这样做:

<form><!--blah blah--></form>
<script type="text/js" src="somewhere"></script>

这些是缺点:

  1. 您正在添加一个额外的 HTTP 请求!!为什么?
  2. 您将其与表单的加载完成事件异步,这使其不准确
  3. 您有没有想过可能您的网络服务器在那个特定时间拒绝了您客户的请求?那么会发生什么?它会破坏您的服务器端脚本!
  4. 如果您在服务器端打印表单,为什么不在服务器端进行这些更改?

【讨论】:

  • 你假设他正在使用 jQuery。情况可能并非如此。
  • 好吧,他可以随意忽略 jQuery 的使用!他的电话,我只是提供一个解决方案,嗯,是的,这表明我对 jQuery 过度沉迷:(
  • @Truth 哦,天哪!我以前从未见过那张照片!这让我很开心 :D 我将编辑我的回复以使用无 jQuery 的方法。谢谢真相。
  • 添加了纯 javascript,现在我对纯代码感到满意:D
  • 3p3r,你怎么知道davorp用的是php?在您的示例中,您使用'recaptchalib.php' 女巫通过调用函数recaptcha_get_html 创建表单(html)内容。 (a) 这就是我在回答中所描述的,但没有提及code.google.com/p/recaptcha (b) 您将我在表单字段中的 id 提议更改为直接更改属性。 (c) 最后,由于“recaptchalib.php”是本地的,整个表单生成应该只发生一次对 recaptcha_get_html 的调用,修改为接受表单名称和操作作为参数。对不起,但我为您的回答真实性 -1。
【解决方案5】:

您想要一种处理验证码的通用方法,因此,如果您决定使用 reCAPTCHA 以外的其他方法,您可以以同样的方式处理它。我觉得你的想法很好,但是你抽象的太深了。

理想情况下,您需要一些黑盒验证码系统,它只是做它的事情,而您不必担心盒子里有哪个验证码插件。您唯一需要摆脱该框的就是用户输入的内容是否正确。

如果您试图指示黑匣子如何进行此项检查,那么您就是在搞砸黑匣子。此外,如果您最终切换到不同的验证码插件,您也将不得不使用该插件。

出于这些原因,我建议你只抽象出一件事:is_human_user 方法(或任何你想调用的方法)。您需要做的就是为您决定使用的每个验证码插件实现此方法。对于 reCAPTCHA,听起来这个方法会返回 recaptcha_challenge_field == recaptcha_response_field。然后,无论检查什么都只是调用is_human_user,并且对实现是什么视而不见。

如果这不是您要寻找的答案,我很抱歉,但退后一步并提醒自己为什么会落入那个兔子洞总是很重要的。

【讨论】:

    【解决方案6】:

    我相信你不能抽象太多的验证码实现,因为每个验证码都使用它自己的验证功能,无论如何更改客户端验证码字段的名称并不是最好的解决方案,因为如果客户端不启用 JavaScript 根本无法工作。

    考虑到这一点,我认为提取字段名称的最佳方法是在服务器端使用变量,在您收到表单的帖子后,您可以这样做:

    $captchaId = $_POST["recaptcha_challenge_field"];
    $captchaUserResponse = $_POST["recaptcha_response_field"];
    

    然后将其传递给这样的通用验证函数:

    recaptcha_generic_check ("PrivateKey", $_SERVER["REMOTE_ADDR"], $captchaId, $captchaUserResponse);
    

    理想情况下,这样您只需要更改帖子中收到的字段的名称(当然还有功能),一切都将保持不变。

    【讨论】:

    • 这基本上是 3p3r 建议的 (stackoverflow.com/a/10982928/41939),但 9 小时后。 :)
    • 不,3p3r 建议使用 php 脚本来重命名浏览器中的字段,我建议只需将帖子中的传入字段分配给一个变量并将其传递给验证码验证函数。我相信我错过了您想为多个站点提供登录表单,无论如何我相信这种方法可以帮助您,因为您可能通过传入的验证码字段知道验证码解决方案正在使用客户端,而使用 3p3r 方法您可能会丢失该信息除非您使用另一个参数指定它。
    • 您正在寻找他的旧答案,较新的答案是提出服务器端映射。没有客户端重命名,只是用我自己的服务器端生成的 javascript 包装了 recaptcha javascript:
    猜你喜欢
    • 1970-01-01
    • 2020-02-20
    • 2021-06-12
    • 1970-01-01
    • 2010-12-26
    • 1970-01-01
    • 2016-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多