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

Ele*_*One 33 javascript jquery google-chrome backbone.js

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

以前曾多次提出类似的问题,不同的形式 - 例如,

如何让浏览器提示保存密码?

浏览器如何知道何时提示用户保存密码?

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

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

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

可以在这里找到一个jsfiddle ,但它没什么用处,因为你必须dummy.html在本地创建才能看到所描述的行为.因此,最好的方法是将完整的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>
Run Code Online (Sandbox Code Playgroud)

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

编辑:请注意,Chrome的"保存密码问题"已扩展到使用由Google开发的AngularJS的用户:Github

mku*_*urz 23

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

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

在推送状态或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>
Run Code Online (Sandbox Code Playgroud)

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

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

  • 请注意,在使用不受信任的SSL证书时,Chrome似乎不会提供此提示来保存凭据.希望这有助于其他开发人员在那里撞击桌子,试图解决这个问题. (12认同)

Ele*_*One 8

在彻底研究了这个问题之后,这是我的最终报告:

首先,问题中突出显示的问题实际上是红鲱鱼.我被错误地提交表单到iframe(这附庸风雅突出-但我没有连接点).我对这个问题的处理方法是基于这个例子,其中一些其他相关答案也被引用.这里可以看到正确的方法.基本上,actionform应设置到srciframe(这正是@arty建议).

当你这样做时,问题中突出显示的特定问题就会消失,因为页面根本没有重新加载(这是有道理的 - 为什么在你提交的时候页面会重新加载iframe?它应该没有,这应该给我提示关闭).无论如何,因为页面没有重新加载,Chrome不会要求您保存密码,无论您等待显示表单多久onDomReady.

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

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

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

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

(3)表单提交到一个简单重定向到该index.php文件的PHP 文件

这种方法有两个优点:

(1)适用于Chrome.

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

这是相关代码(简化):

的index.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>
Run Code Online (Sandbox Code Playgroud)

MYCODE.JS

function createForm() {

    VLoginForm = Backbone.View.extend({

        allowDefaultSubmit : true,

        // UI events from the HTML created by this view
        events : {
            "click button[name=login]" : "onClickLogin",
            "submit form" : "onFormSubmit"
        },

        initialize : function() {
            this.verified = false;
            // this would be in a template
            this.html = "<form method='post' action='dummy.php'><p><label for='email'>Email</label><input type='text' name='email'></p><p><label for='password'>Password<input type='password' name='password'></p><button name='login'>Login</button></form>";        
        },
        render : function() {
            this.$el.html(this.html);
            return this;
        },
        onClickLogin : function(event) {

            // verify the data with an AJAX call (not included)

            if ( verifiedWithAJAX ) {
                this.verified = true;
                this.$("form").submit();
            }           
            else {
                // not verified, output a message
            }

            // we may have been called manually, so double check
            // that we have an event to stop.
            if ( event ) {
                event.preventDefault();
                event.stopPropagation();
            }

        },
        onFormSubmit : function(event) {
            if ( !this.verified ) {             
                event.preventDefault();
                event.stopPropagation();
                this.onClickLogin();
            }
            else if ( this.allowDefaultSubmit ) {
                // submits the form as per default
            }
            else {
                // do your own thing...
                event.preventDefault();
                event.stopPropagation();
            }
        }
    });

    var form = new VLoginForm();
    $("#form").html(form.render().$el);

}
Run Code Online (Sandbox Code Playgroud)

DUMMY.PHP

<?php
    header("Location: index.php");
?>
Run Code Online (Sandbox Code Playgroud)

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