使用AJAX登录时让Chrome提示保存密码

时间:2014-01-17 16:48:15

标签: javascript jquery google-chrome backbone.js

注意:此问题已在其原始版本中进行了大量编辑。这个问题已经大大简化了。

之前已经多次提出类似的问题,不同的形式 - 例如,

How can I get browser to prompt to save password?

How does browser know when to prompt user to save password?

然而,这个问题涉及到Chrome功能的一个非常具体的方面,所以在这方面它是完全不同的。

根据过去的答案判断,让Chrome提示输入密码的最佳方法是将表单提交给虚拟iframe,同时实际通过AJAX登录:{{3} }。这对我来说很有意义,所以我现在已经花了几天的时间来摆弄一些示例代码。但是,Chrome在这方面的行为对我来说没有意义。完全没有。因此问题。

出于某种原因,如果在 onDomReady 之后/之后存在提交到虚拟iframe的表单,则Chrome不会提示您保存密码。

可以找到一个jsfiddle example,但它没什么用处,因为你必须在本地创建dummy.html才能看到所描述的行为。因此,查看此内容的最佳方法是将完整的html复制到您自己的index.html中,然后再创建一个dummy.html文件。

以下是index.html的完整代码。这三种方法突出显示为(1)(2)(3)。只有(2)才能确保提示用户保存自己的密码,(3)不起作用的事实对我来说特别困惑。

<html>
<head>
<title>Chrome: Remember Password</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>

<script type="text/javascript">  

    $(function(){

        function create_login_form()
        {
            $('#login_form').html(
            "<input type='text' name='email'>" +
            "<input type='password' name='password'>" +
            "<input id='login-button' type='submit' value='Login'/>");
        }

        // (1) this does not work. chrome seems to require time after "onDomReady" to process
        // the forms that are present on the page. if the form fields exist while that processing
        // is going on, they will not generate the "Save Password?" prompt.
        //create_login_form();

        // (2) This works. chrome has obviously finished its "work" on the form fields by now so
        // we can add our content to the form and, upon submit, it will prompt "Save Password?"

        setTimeout(create_login_form,500);

    });

</script>
</head>
<body>

<!-- Our dummy iframe where the form submits to -->
<iframe src="dummy.html" name="dummy" style="display: none"></iframe>

<form action="" method="post" target="dummy" id="login_form">

    <!-- (3) fails. form content cannot be present on pageload -->
    <!--
    <input type='text' name='email'>
    <input type='password' name='password'>
    <input id='login-button' type='submit' value='Login'/>      
    -->

</form>

</body> 
</html>

如果有人能够解释这里发生了什么,我会非常感激。

编辑:请注意,Chrome的“保存密码问题”已扩展到使用由Google开发的AngularJS的人员:here

6 个答案:

答案 0 :(得分:23)

从Chrome 46开始,您不再需要iframe黑客了!

所有相应的Chrome问题都已修复:1 2 3

只需确保原始登录表单不存在&#34;在推送状态或ajax请求之后,通过删除(或隐藏)表单或更改它的动作URL(没有测试但也应该工作)。还要确保同一页面中的所有其他表单都指向不同的操作URL,否则它们也会被视为登录表单。

看看这个例子:

<!doctype html>
<title>dynamic</title>
<button onclick="addGeneratedForms()">addGeneratedForms</button>
<script>
function addGeneratedForms(){
  var div = document.createElement('div');
  div.innerHTML = '<form class="login" method="post" action="login">\
    <input type="text" name="username">\
    <input type="password" name="password">\
    <button type="submit">login</button> stay on this page but update the url with pushState\
  </form>';
  document.body.appendChild(div);
}
document.body.addEventListener('submit', function ajax(e){
  e.preventDefault();
  setTimeout(function(){
      e.target.parentNode.removeChild(e.target); // Or alternatively just hide the form: e.target.style.display = 'none';

      history.replaceState({success:true}, 'title', "/success.html");

      // This is working too!!! (uncomment the history.xxxState(..) line above) (it works when the http response is a redirect or a 200 status)
      //var request = new XMLHttpRequest();
      //request.open('POST', '/success.html', true); // use a real url you have instead of '/success.html'
      //request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
      //request.send();
  }, 1);
}, false);
</script>

如果您有兴趣,可以在this repo中找到更多示例。您可以使用节点node server.js运行它。 也许还可以看到此提交的评论:https://github.com/mkurz/ajax-login/commit/c0d9503c1d2a6a3a052f337b8cad2259033b1a58

如果您需要帮助,请告诉我。

答案 1 :(得分:8)

在仔细研究这个问题之后,这是我的最终报告:

首先,问题中突出显示的问题实际上是红鲱鱼。我错误地将表单提交给iframearty突出显示 - 但我没有连接点)。我对此问题的处理方法基于this example,其中一些其他相关答案也引用了。可以看到正确的方法here。基本上,action的{​​{1}}应设置为form的{​​{1}}(这正是@arty建议的那样)。

当你这样做时,问题中突出显示的特定问题就会消失,因为页面根本没有重新加载(这是有道理的 - 为什么在你提交src时页面会重新加载?那应该让我失望了。无论如何,由于页面无法重新加载,因此无论您在iframe之后等待显示表单多长时间,Chrome都不会要求您保存密码。

因此,提交至iframe(正确)将永远不会导致Chrome要求您保存密码。 Chrome只会在包含该表单的页面重新加载时才会这样做(注意:与此处的旧帖子相反,如果动态创建表单,Chrome会要求您保存密码。)

因此,唯一的解决方案是在提交表单时强制重新加载页面。但是我们如何做到这一点并保持我们的AJAX / SPA结构完好无损?这是我的解决方案:

(1)将SPA分为两部分:(1)对于未登录的用户,(2)对于已登录的用户。对于那些拥有较大网站的人来说,这可能是不可行的。 我不认为这是一个永久的解决方案 - 请Chrome,请...修复此错误。

(2)我在表单上捕获了两个事件:onDomReadyiframe。特别是对于登录表单,我抓取onClickSaveButton上的用户详细信息并进行AJAX调用以验证其信息。如果该信息通过,那么我手动调用onFormSubmitonClickSaveButton,我确保在调用formName.submit()之前未提交表单。

(3)表单提交到一个只重定向到onFormSubmit文件的PHP文件

这种方法有两个好处:

(1)适用于Chrome。

(2)在Firefox上,用户现在只有在成功登录后才被要求保存密码(我个人总是觉得在出错时要求保存我的密码非常烦人。)

以下是相关代码(简化):

<强>的index.php

onClickSaveButton

<强> MYCODE.JS

index.php

<强> DUMMY.PHP

<html>
<head>
<title>Chrome: Remember Password</title>

<!-- dependencies -->
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="http://underscorejs.org/underscore.js"></script>
<script type="text/javascript" src="http://backbonejs.org/backbone.js"></script>

<!-- app code -->
<script type="text/javascript" src="mycode.js"></script>
<script>

    $(function(){

        createForm();

    });

</script>

</head>
<body>

    <div id='header'>
        <h1>Welcome to my page!</h1>
    </div>

    <div id='content'>
        <div id='form'>
        </div>
    </div>

</body> 
</html>

编辑: mkurz的答案看起来很有希望。也许错误是固定的。

答案 2 :(得分:1)

您可以让Chrome(v39)显示密码保存提示,而无需重新加载。只需将表单提交给iframe,其中src是一个空白页面,具有Content-Type: text/html标题(省略标题或使用text/plain两者似乎都有效。)< / p>

不知道这是一个错误,还是预期的行为。如果是前者,请保持安静: - )

答案 3 :(得分:0)

我正在使用Chrome 32.0。这两种方法在这里工作正常,无法理解为什么它最终会有所不同......

那就是说,我经常不得不使用setTimeout()几百毫秒来制作例如focus()按预期工作。那对你来说也许可以解决问题。在你的情况下:

setTimeout(display_login_form(),500)

答案 4 :(得分:0)

我们现在可以为此使用实验性 API:凭据管理API

Official example

My another answer with code snippet

答案 5 :(得分:0)

我正在使用SPA应用程序,并且在仅通过AJAX请求传递用户凭据时,如何强制Chrome存储用户凭据存在相同的问题。因此,我使用navigator.credentials来调用默认的Chrome对话框来存储网站的凭据。它像魅力一样起作用!

皮包骨头的例子。

HTML:

<form id="credential-form">
    <input type="text" name="username" required autocomplete="username">
    <input type="password" name="password" required autocomplete="current-password">
</form>

JS:

let credentialForm = document.getElementById('credential-form');
let credential = new PasswordCredential(credentialForm);
navigator.credentials.store(credential);

有关完整示例,请参见我在其中找到此解决方案的here