登录流程需要完成以下三步
- 实现异步登录;
- 登录成功后调用凭证管理 API 进行登录信息存储;
- 完成登录成功后页面切换或者是 UI 更新之类的操作。
凭据存储
调用 navigator.credentials.store() 这个方法进行登录信息存储。由于仅有部分浏览器支持凭据管理 API,因此在使用前需要进行方法是否存在的判断:
if (navigator.credentials) {
// 使用 navigator.credentials.store 进行凭证存储
}
- 凭证管理 API 提供了两凭据对象:PasswordCredential 和 FederatedCredential,这两种凭据对象均实现了 Credential 的接口,可以分别针对 账号密码登录 和 第三方登录 两种模式的登录信息进行存储。
凭据获取
- 过 navigator.credentials.get() 方法,可以获取同个域名下用户存储的登录信息。
if (navigator.credentials) {
navigator.credentials.get({
password: true
});
}
- 参数
- password: {boolean} 是否支持通过密码认证登录
- federated: 第三方登录 {Object}
- providers: {Array} 联合登录账号供应者 id 组成的数组
- unmediated: {boolean} 是否显示账号选择器 如果只有一个账号,自动填充
获取账号密码登录凭证
只有当 options.password === true 时,账号选择器才会展示类型为 PasswordCredential 的登录信息。
退出登录
- 当用户退出网站时,应该确保用户在下次访问的时候不会自动登录。可以通过调用 navigator.credentials.requireUserMediation() 或 navigator.credentials.preventSilentAccess() 来关闭自动登录。Chrome 60+ 后,requireUserMediation 被重命名为 preventSilentAccess,请注意兼容。
app.logout = function () {
// 处理登出流程
if (navigator.credentials.requireUserMediation) {
navigator.credentials.requireUserMediation();
}
else if (navigator.credentials.preventSilentAccess) {
navigator.credentials.preventSilentAccess();
}
};
栗子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form id="test-form">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="提交">
</form>
<button>退出登录(不保存凭证)</button>
<!-- <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> -->
<script>
var form = document.querySelector('#test-form');
var username = document.querySelector('input[name=username]');
var password = document.querySelector('input[name=password]');
var btn = document.querySelector('button');
form.addEventListener('submit', function(e) {
e.preventDefault();
// $.ajax({
// url: './test.php',
// type: 'json',
// data: {
// username: formData.get('username'),
// password: formData.get('password')
// },
// success: function(res) {
// console.log('ok', res)
// // 后续操作
// },
// error: function(err) {
// console.log('err', err)
// // 错误操作
// }
// })
// return false;
var formData = new FormData(form);
fetch('./test.php', {
method: "POST",
body: formData,
mode: "no-cors",
cache: "force-cache"
})
.then(res => {
console.log('first', res);
if (res.status === 200) {
return res.json();
}
return Promise.reject(res.status);
})
.then(data => {
saveCredentials(data, formData.get('username'), formData.get('password'));
console.log('second', data);
})
.catch(err => {
console.log('fetch is Failed: ' + err);
})
})
function saveCredentials(data, username, password) {
if (navigator.credentials) {
var cred = new PasswordCredential({
id: username,
password: password,
// name: username, // 可选
// iconURL: data.icon // 可选
});
navigator.credentials.store(cred).then(cred => {
console.log('cred', cred);
// 后续操作
});
}
}
if (navigator.credentials) {
navigator.credentials.get({
password: true,
// federated: { // 第三方登录
// providers: ['https://www.baidu.com']
// },
unmediated: true // 是否显示账号选择器 (只有一个账号的情况下触发, 可以用登出取消自动填充)
})
.then(cred => {
// cred 可能为 undefined
if (cred) {
switch (cred.type) {
case 'password':
// 对 PasswordCredential 凭证进行处理
console.log(cred)
username.value = cred.id;
password.value = cred.password;
break;
default:
break;
}
}
});
}
btn.addEventListener('click', () => {
// 处理登出流程 (取消自动登录)
if (navigator.credentials.requireUserMediation) {
navigator.credentials.requireUserMediation().then(() => {
console.log('登出成功')
})
.catch(() => {
console.log('登出失败')
});;
} else if (navigator.credentials.preventSilentAccess) {
navigator.credentials.preventSilentAccess().then(() => {
console.log('登出成功')
})
.catch(() => {
console.log('登出失败')
});;
}
})
</script>
</body>
</html>
参考来源: https://lavas.baidu.com/pwa/automatic-login/credential-management-api/introduction