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

175

嘿,我正在开发一个Web应用程序,其中登录对话框的工作方式如下:

  1. 用户点击“登录”
  2. 使用AJAX加载登录表单HTML,并在页面上的DIV中显示
  3. 用户在字段中输入用户名/密码并单击提交。它不是一个<form>-- 用户名/密码通过AJAX提交
  4. 如果用户名/密码正确,则页面会重新加载并显示已登录的用户。
  5. 如果用户名/密码错误,则页面不会重新加载,但错误消息将出现在DIV中,并允许用户再次尝试。

问题是:与其他网站一样,浏览器从未提供通常的“保存此密码?是/永不/现在不”提示。

我尝试使用“autocomplete ='on'”将<div>包装在<form>标记中,但没有任何变化。

是否可能让浏览器在不重大改造我的登录流程的情况下提示存储密码?

谢谢 埃里克

p.s.为了补充我的问题,我肯定正在使用存储密码的浏览器,而且我从未单击过“永不为此网站”,...这是浏览器无法检测到它是登录表单而不是操作员错误的技术问题:-)


不要忘记并非所有浏览器都能存储密码。 - Buhake Sindi
1
也许浏览器提供了保存您的用户名和密码的选项,而您点击了“不再询问!”? - clyfe
1
相关链接:https://dev59.com/UFfUa4cB1Zd3GeqPJp1j - user123444555621
22个回答

75

我找到了这个问题的完整解决方案。(我已经在 Chrome 27 和 Firefox 21 中测试过。)

需要知道两件事情:

  1. 触发“保存密码”功能,以及
  2. 恢复保存的用户名和密码。

1. 触发“保存密码”功能:

对于Firefox 21,当检测到一个包含输入文本框和输入密码框的表单被提交时,就会触发“保存密码”功能。所以我们只需要使用

$('#loginButton').click(someFunctionForLogin);
$('#loginForm').submit(function(event){event.preventDefault();});

someFunctionForLogin()通过ajax登录并重新加载/重定向到已登录页面,而event.preventDefault()阻止了由于提交表单而进行的原始重定向。

如果您只处理Firefox,则上述解决方案就足够了,但在Chrome 27中无法正常工作。然后您会问如何在Chrome 27中触发“保存密码”。

对于Chrome 27,“保存密码”是在提交包含输入文本字段属性名为'username'和输入密码字段属性名为'password'的表单后重定向到该页面时触发的。因此,我们无法阻止由于提交表单而进行的重定向,但是我们可以在完成ajax登录后进行重定向。(如果您希望ajax登录不重新加载页面或不重定向到页面,则很遗憾,我的解决方案无法解决。)然后,我们可以使用

<form id='loginForm' action='signedIn.xxx' method='post'>
    <input type='text' name='username'>
    <input type='password' name='password'>
    <button id='loginButton' type='button'>Login</button>
</form>
<script>
    $('#loginButton').click(someFunctionForLogin);
    function someFunctionForLogin(){
        if(/*ajax login success*/) {
            $('#loginForm').submit();
        }
        else {
            //do something to show login fail(e.g. display fail messages)
        }
    }
</script>

使用 type='button' 的按钮在点击时不会提交表单。然后,将一个函数绑定到按钮实现ajax登录。最后,调用 $('#loginForm').submit(); 将页面重定向到已登录页面。如果已登录页面和当前页面相同,则可以将 'signedIn.xxx' 替换为当前页面以进行“刷新”。

现在,你会发现 Chrome 27 的方法也适用于 Firefox 21。因此最好使用它。

2. 恢复已保存的用户名/密码:

如果您已经在HTML中硬编码了 loginForm,则恢复已保存的密码并不会有任何问题。
但是,如果您使用js/jquery来动态创建 loginForm,保存的用户名/密码将不会绑定到 loginForm,因为只有在文档加载时才会绑定保存的用户名/密码。
因此,您需要将 loginForm 硬编码为HTML并使用js/jquery来动态移动/显示/隐藏 loginForm。


备注: 如果您使用 ajax 登录,请不要在 form 标签中添加 autocomplete='off'。

<form id='loginForm' action='signedIn.xxx' autocomplete='off'>

autocomplete='off'会使得恢复用户名/密码到登录表单失败,因为您不允许其自动填写用户名/密码。


2
在Chrome中,'event.preventDefault()'防止显示“保存密码”提示框,参见此错误:https://code.google.com/p/chromium/issues/detail?id=282488 - mkurz
2
许多用户使用“ENTER”键提交表单,由于您在用户提交表单时“阻止默认操作”,因此什么也不会发生,这是糟糕的用户体验。您可以在提交时检查“keycode”以获取Enter键,但是您将再次回到起点... - David Reinberger
2
Chrome 46修复了其错误行为 - 不再需要任何解决方法。请参见https://dev59.com/tGEi5IYBdhLWcg3whcri#33113374。 - mkurz
有没有一些文档可以让我看到 Google Chrome 允许的 name 属性值是什么?目前我的字段名为 identity,但 Chrome 没有提示保存密码对话框。 - CodeBrauer
1
无法可靠地工作,如果您从Javascript提交,则某些浏览器不会触发“记住”功能。 - JustAMartin
显示剩余2条评论

50

使用 按钮 登录:

如果您使用带有 onclick 处理程序的 type="button" 来使用 ajax 登录,则浏览器不会提供保存密码的选项。

<form id="loginform">
 <input name="username" type="text" />
 <input name="password" type="password" />
 <input name="doLogin"  type="button" value="Login" onclick="login(this.form);" />
</form>

由于该表单没有提交按钮,且没有操作字段,浏览器不会提示保存密码。


使用提交按钮进行登录:

但是,如果您将按钮更改为type="submit"并处理提交,则浏览器将提示保存密码。

<form id="loginform" action="login.php" onSubmit="return login(this);">
 <input name="username" type="text" />
 <input name="password" type="password" />
 <input name="doLogin"  type="submit" value="Login" />
</form>

使用这种方法,浏览器应该会提供保存密码的选项。


以下是两种方法中使用的Javascript代码:

function login(f){
    var username = f.username.value;
    var password = f.password.value;

    /* Make your validation and ajax magic here. */

    return false; //or the form will post your data to login.php
}

1
@Bigood 因为它对我有效,而被接受的答案则没有。感谢 spetson! - Double M
2
在IE中也适用。我现在可以亲吻你了! - Renra
1
我也试过了,有效果。 :) - Nabeel
1
这是正确的解决问题的方法,也是最优雅的解决方案。 - Arash Kazemi
它的工作原因是我只需将第二段代码复制并通过检查元素粘贴到登录页面中,在该页面上输入用户名/密码并按登录,然后它提示我保存用户/密码。然后我刷新页面,哇,它提示我选择已保存的用户/密码 ;) - Dewlance

15

我自己一直在苦苦挣扎,终于能够找到问题所在以及导致失败的原因。

这一切都源于我的登录表单是通过backbone.js动态注入到页面中的。当我将我的登录表单直接嵌入到index.html文件中时,一切都像魔法般正常工作了。

我认为这是因为浏览器必须知道存在一个现有的登录表单,但由于我的表单是动态注入到页面中的,它不知道是否真正存在“真实”的登录表单。


从Chrome 46版本开始,您现在也可以动态注入表单,并将其识别为“真实”的登录表单,可以保存凭据。请参见https://dev59.com/tGEi5IYBdhLWcg3whcri#33113374。 - mkurz

12

这个解决方案由Eric在codingforums上发布,对我很有用。


它不弹出的原因是浏览器需要页面物理刷新回到服务器。一个小技巧是在表单中执行两个动作。第一个动作是在提交时调用您的Ajax代码。另外让表单目标指向一个隐藏的iframe。

代码:

<iframe src="ablankpage.htm" id="temp" name="temp" style="display:none"></iframe>
<form target="temp" onsubmit="yourAjaxCall();">

看看是否会出现提示。

Eric


发表于 http://www.codingforums.com/showthread.php?t=123007


5
请注意,我发现Chrome 11无法在表单提交到自身时提供保存凭据的选项。因此,您需要将action设置为某些虚拟页面。 - user123444555621
这将在URL中以GET请求的方式发送密码明文。如果将方法更改为POST,Firefox(16)将在新标签页中打开先前隐藏的框架。对于Android 4上的Chrome也是如此。 :-( - stefan.s
Chrome 46修复了其错误行为 - 不再需要iframe的解决方法。请参见https://dev59.com/tGEi5IYBdhLWcg3whcri#33113374。 - mkurz

12

简单的2020方法

这将自动启用浏览器中的自动完成并保存密码。

  • autocomplete="on" (表单)
  • autocomplete="username" (输入框, 邮箱/用户名)
  • autocomplete="current-password" (输入框, 密码)
<form autocomplete="on">
  <input id="user-text-field" type="email" autocomplete="username"/>
  <input id="password-text-field" type="password" autocomplete="current-password"/>
</form>

请查看苹果官方文档了解更多信息: 在HTML输入元素上启用密码自动填充


11
FYI,这似乎需要提交表单(无法使用XHR和“preventDefault”),如果只有一个密码字段(数字保险箱类型的应用程序),它将无法正常工作。分享这些发现给其他可能会发现有用的人。 - Tomáš Hübelbauer

11

有一种终极解决方案,可以强制所有浏览器(已测试:chrome 25、safari 5.1、IE10、Firefox 16)使用jQueryajax请求来询问是否保存密码:

JS:

$(document).ready(function() {
    $('form').bind('submit', $('form'), function(event) {
        var form = this;

        event.preventDefault();
        event.stopPropagation();

        if (form.submitted) {
            return;
        }

        form.submitted = true;

        $.ajax({
            url: '/login/api/jsonrpc/',
            data: {
                username: $('input[name=username]').val(),
                password: $('input[name=password]').val()
            },
            success: function(response) {
                form.submitted = false;
                form.submit(); //invoke the save password in browser
            }
        });
    });
});

HTML:

<form id="loginform" action="login.php" autocomplete="on">
    <label for="username">Username</label>
    <input name="username" type="text" value="" autocomplete="on" />
    <label for="password">Password</label>
    <input name="password" type="password" value="" autocomplete="on" />
   <input type="submit" name="doLogin" value="Login" />
</form>

关键在于阻止表单按照自己的方式提交(event.stopPropagation()),而是发送自己的代码($.ajax()), 并在ajax的成功回调函数中再次提交表单,这样浏览器就能捕获它并显示要求保存密码的请求。您也可以添加一些错误处理程序等。

希望对某些人有帮助。


8
这实际上会执行一个 HTTP get 请求到 login.php?username=username&password=password,这样做完全违背了最初保存密码的目的。 - DATEx2
哇!这对我起作用了;)非常感谢! - Hardik Thaker
1
是的,在表单中添加“事件监听器”,并添加“event.preventDefault()”和“event.stopPropagation()”。 - Dennis S

7

我尝试了 spetson的答案,但在Chrome 18上对我无效。有效的方法是向iframe添加一个加载处理程序,并不中断提交(jQuery 1.7):

function getSessions() {
    $.getJSON("sessions", function (data, textStatus) {
        // Do stuff
    }).error(function () { $('#loginForm').fadeIn(); });
}
$('form', '#loginForm').submit(function (e) {
    $('#loginForm').fadeOut();
}); 
$('#loginframe').on('load', getSessions);
getSessions();

HTML:

<div id="loginForm">
    <h3>Please log in</h3>
    <form action="/login" method="post" target="loginframe">
            <label>Username :</label>
            <input type="text" name="login" id="username" />
            <label>Password :</label>
            <input type="password" name="password" id="password"/>
            <br/>
            <button type="submit" id="loginB" name="loginB">Login!</button>
    </form>
</div>
<iframe id="loginframe" name="loginframe"></iframe>

getSessions()将执行AJAX调用,如果失败则显示loginForm div。(如果用户未经过身份验证,Web服务将返回403)。

已在FF和IE8中进行测试,可正常工作。


我在Chrome中尝试了这种方法,但它不起作用,除非操作页面(例如您的示例中的/login)返回一些文本或其他可见内容。 - Stephen Bunch
1
Chrome 46修复了其错误行为 - 不再需要iframe的解决方法。请参见https://dev59.com/tGEi5IYBdhLWcg3whcri#33113374。 - mkurz

6

目前的回答没有清楚地表明您可以使用HTML5历史记录API来提示保存密码。

首先,您需要确保至少有一个带有密码和电子邮件或用户名字段的<form>元素。只要使用正确的输入类型(密码、电子邮件或用户名),大多数浏览器会自动处理此问题。但是为了确保,请为每个输入元素正确设置自动完成值。

您可以在此处找到自动完成值列表:https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete

您需要的是:usernameemailcurrent-password

然后您有两种可能性:

  • 如果提交后导航到不同的URL,则大多数浏览器都会提示保存密码。
  • 如果您不想重定向到不同的URL甚至重新加载页面(例如单页应用程序)。只需在表单的提交处理程序中防止事件默认值(使用e.preventDefault)。您可以使用HTML5历史API将某些内容推送到历史记录中,以指示您在单个页面应用程序内进行了“导航”。现在浏览器将提示保存密码和用户名。
history.pushState({}, "Your new page title");

您也可以更改页面的URL,但这不是提示保存密码所必需的:

history.pushState({}, "Your new page title", "new-url");

文档:https://developer.mozilla.org/en-US/docs/Web/API/History/pushState

这样做的另一个好处是,如果用户输入密码不正确,您可以防止浏览器询问是否保存密码。请注意,在某些浏览器中,即使调用 preventDefault 并且不使用历史记录 API,浏览器仍然会一直要求保存凭据。

如果您不想导航离开或修改浏览器历史记录,则可以改用 replaceState(这也有效)。


这是2020年的答案!我必须使用 history.pushState({}, null, "您的新页面标题"); 来更改URL而不进行任何导航。 - Delcon

6
浏览器可能无法检测到您的表单是登录表单。根据this previous question中的一些讨论,浏览器会寻找看起来像<input type="password">的表单字段。您的密码表单字段是否实现类似于此?
编辑:为了回答您下面的问题,我认为Firefox通过form.elements[n].type == "password"(迭代所有表单元素)来检测密码,然后通过向后搜索表单元素以查找在密码字段之前的文本字段来检测用户名字段(更多信息here)。据我所知,您的登录表单需要成为<form>的一部分,否则Firefox将无法检测到它。

3
很相似,谢谢,我会接受这个作为官方答案,因为这已经是我们能得到的最接近的了。有一件令人沮丧的事情:我似乎找不到(在网上任何地方)一个简单的博客文章,列出浏览器用来检测你正在填写的表单是否是登录表单的规则,以便他们可以提供保存用户密码的选项。考虑到这是一种黑魔法(而且至少Mozilla是开源的),你认为有人会发布这些启发式规则。 - Eric
(同样地,似乎没有办法“提示”我的登录表单,以便浏览器知道它是一个登录表单。再次感到惊讶的是,这在网络上并没有得到更好的记录。我认为我的更改将针对表单字段名称和整体HTML结构,然后我希望[!]这可以解决问题。) - Eric
好的,没什么运气。我已经提出了一个新问题,从稍微不同的角度来解决这个问题:https://dev59.com/mnE95IYBdhLWcg3wPbZ7 - Eric

2
我花了很多时间阅读这个主题上的各种答案,对我来说,实际上是有些不同的(相关但不同)。在移动Safari(iOS设备)上,如果登录表单在页面加载时是隐藏的,则提示框不会出现(在显示表单然后提交它之后)。你可以使用以下代码进行测试,在页面加载后5秒钟显示表单。删除JS和display:none,它就可以工作了。我还没有找到解决方法,只是发布了一下,以防其他人有相同的问题并且无法找出原因。
$(function() {
  setTimeout(function() {
    $('form').fadeIn();
  }, 5000);
});

HTML:

<form method="POST" style="display: none;">
  <input name='email' id='email' type='email' placeholder='email' />
  <input name='password' id='password' type='password' placeholder='password' />
  <button type="submit">LOGIN</button>
</form>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接