Запрещаем браузеру Google Chrome сохранять и предлагать сохраненный пароль в нашем приложении на Vue.js(v2)
Мир стоит вверх ногами. Корпорация добра печется о нашей безопасности. И в то же время делает все, чтобы наши секреты стали достоянием гласности. Это я о политике гугл в плане сохранения логин-паролей в браузере, через него в аккаунте и о доступности всего нашего секретного добра всем, кто сможет подойти к нашим компьютерам.
Да, удобно. Но небезопасно. И временами очень вредно.
Запустил я очередной проект на работе. И самой большой проблемой по опыту старого проекта было то, что сотрудники вовсю пользовались автосохраненим паролей, поэтому под Васей мог сидеть Сергей и ни о какой нормальной аутентификации, а тем более разраничении прав доступа речи идти не могло.
Сначала двинулся в сторону того же гугла — как отключить или запретить автосохранение или автоподстановку сохраненного пароля? Старые рецепты с autocomplete="off"
и autocomplete="new-password"
уже не работают, последний работающий рецепт оказался прост — головную боль следует лечить усекновением болящей. То есть глобальным отключением автосохранения. Но это не вариант. Пусть юзеры дальше балуются своими паролями от почты, соцсетей и прочих радостей жизни, мне не жалко.
Далее последовал легкий инжиниринг. И вот какой результат — если на страничке есть input
type="password"
, то при появлении первого символа в адресной строке появляется ключик. И все. Что ты ни делай, какие html тэги ни прописывай — есть password
, значит, надо бежать и барину кланяться, предлагая все мыслимые и немыслимые блага от того, что барин позволит сохранить пароль. Ну и потом барин будет весьма доволен, что пароль не надо вспоминать, а просто его вставить.
Хмм... подумал я... И решил — раз у хрома такая нервная реакция на type="password"
, то надо просто от него отказаться и всех делов то!
Сказано — сделано. Изваял компонент, который незамысловато делает простейшую вещь — на экран выводит точечки, а в v-model передает то, что введено. Ну и плюсом отрабатывает клавишу Enter.
Конечно, не обошлось без ограничений. Но они очень малы и незначительны — нельзя курсор перемещать стрелками, только backspace. Но, учитывая, что под точками и так не видно, где и что надо подправить — вероятность испытать колоссальное неудобство от такого ограничения стремится к нулю. Не стал делать кнопочку с глазиком, чтобы посмотреть, что же я ввел, label
для input
оформил в стиле компонента vuetify, то есть текст-подсказка уползает вверх при фокусе и возвращается при утере фокуса при условии, что ничего не было введено.
Ну и использование такого компонента тоже очень простое — создать .vue файл, импортировать в проект и использовать в шаблоне.
Теперь, собственно, код.
Код под спойлером
<template>
<div class="logpass">
<input type="hidden" v-model="opass">
<label :for="inpid">Пароль</label>
<input style="width: 100%"
:id="inpid"
type="text"
v-model="ipass"
@input="input"
@keydown="keyDown"
@focus="focus"
@blur="blur"
@keydown.enter="$emit('keydown_enter',$event)">
</div>
</template>
<script>
export default {
name: "login-password",
data() {
return {
ipass:'', // input password
opass:'', // output password
}
},
computed: {
inpid(){
let length=7
let result
let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let el=''
while(el!==null){ // void to duplicate id if node with same id exists
result=letters.charAt(Math.floor(Math.random() *
letters.length));
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() *
characters.length));
}
el=document.querySelector('#'+result)
}
return result;
},
},
methods: {
focus(event){
let el=document.querySelector(['label[for="'+event.target.id+'"]'])
if(el){
el.classList.add('label-active')
}
},
blur(event){
let el=document.querySelector(['label[for="'+event.target.id+'"]'])
if(el && this.opass==''){
el.classList.remove('label-active')
}
},
input(event){
if(this.opass.length<this.ipass.length){
let s = this.ipass.slice(-1)
this.opass=''.concat(this.opass,s)
s = ''
for(let i=0;i<this.ipass.length;i++)s=''.concat(s,'•')
this.ipass=s
this.$emit('input',this.opass)
}else{
if(this.opass.length>this.ipass.length){
this.opass=this.opass.slice(0,this.ipass.length)
this.$emit('input',this.opass)
}
}
},
keyDown(event){
if(event.key=='ArrowLeft'||event.key=='ArrowRight'||event.key=='ArrowUp'||event.key=='ArrowDown'){
event.preventDefault()
}
},
},
}
</script>
<style scoped lang="scss">
.logpass{
padding-top:14px;
padding-bottom: 12px;
display: flex;
& label{
padding: 5px;
position: absolute;
color: #999;
transition: .3s cubic-bezier(.25,.8,.5,1);
}
& .label-active{
font-size: 10px;
margin-top: -20px;
margin-left: -5px;
}
}
</style>
Как видим — достаточно простая логика — при вводе очередного символа он заменяется на точку и дублируется в переменную, переданную в v-model
. Именно запрет на перемещение курсора в поле ввода позволил реализацию такой логики.
Еще одно замечание — этот компонент не использует инициализацию значением, которое, возможно уже было в v-model
-переменной.
Пример использования:
// какой-нибудь наш модуль на vue, в котором необходим ввод пароля
<template>
<div>
логин: <input type="text" v-model="mylogin">
<login-password v-model="mypassword" @keydown_enter="makeLogin">
</login-password>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
mylogin: '',
mypassword:''
}
},
components: {
LoginPassword:()=>import('./login-password')
},
methods:{
makeLogn(){
.....
}
.......
Ну и напоследок — код свободный, берите, кто хотите, возможно, кому-то идея подойдет на React или Angular.