In questo tutorial vedremo come creare un generatore di password in JavaScript.
Useremo l'approccio Object-Oriented (OO) e sceglieremo di non utilizzare alcun framework o libreria in modo da massimizzare l'aspetto didattico di questo tutorial.
Sostanzialmente dobbiamo generare una stringa casuale di lunghezza n a partire da alcune scelte dell'utente, ossia:
- La stringa deve contenere solo lettere minuscole.
- La stringa deve contenere lettere maiuscole e minuscole.
- La stringa deve contenere numeri.
- La stringa deve contenere caratteri speciali.
Il fatto che la stringa prodotta contenga tutte le opzioni dipende dall'utente. L'utente deve poter regolare la lunghezza della password tramite un campo input di tipo range
e effettuare le sue scelte tramite 4 appositi controlli di tipo checkbox
. Infine, l'utente deve poter copiare la password generata negli appunti tramite uno specifico controllo.
Partiamo con la struttura della nostra classe.
'use strict';
class PasswordGenerator {
constructor({ input = null, createButton = null,
copyButton = null, lengthInput = null,
lettersControl = null,
mixedControl = null,
punctControl = null, numberControl = null }) {
this.letters = {
uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
lowercase: 'abcdefghijklmnopqrstuvwxyz'
};
this.digits = '0123456789';
this.symbols = '!$%&()=?*+><:;.-@[]{}';
this.input = input;
this.createButton = createButton;
this.copyButton = copyButton;
this.lengthInput = lengthInput;
this.lettersControl = lettersControl;
this.mixedControl = mixedControl;
this.punctControl = punctControl;
this.numberControl = numberControl;
if(this.canInit()) {
this.init();
}
}
canInit() {
return this.input && this.createButton && this.copyButton && this.lengthInput && this.lettersControl && this.mixedControl && this.punctControl
&& this.numberControl;
}
//...
}
Nel costruttore definiamo i tipi di carattere che comporranno la password e i riferimenti ai controlli a cui assoceremo gli eventi. Il metodo canInit()
verifica che gli elementi necessari esistano: se questi elementi non esistono, il codice della classe non verrà eseguito.
Poichè non avremo sempre bisogno di tutti i caratteri definiti, creiamo un metodo per effettuare una selezione del tipo richiesto al momento.
getCharacters(kind = 'all') {
switch(kind) {
case 'uppercase':
return this.letters.uppercase;
case 'lowercase':
return this.letters.lowercase;
case 'letters':
return this.letters.uppercase + this.letters.lowercase;
case 'symbols':
return this.symbols;
case 'numbers':
return this.digits;
default:
return this.letters.uppercase + this.letters.lowercase + this.digits + this.symbols;
}
}
Definiamo ora il metodo che, dati un set di caratteri e una lunghezza n, crea una stringa di lunghezza n accedendo in modo casuale al set di caratteri all'interno di un loop.
create(chars, length) {
let password = '';
for(let i = 1; i <= length; i++) {
let char = Math.floor(Math.random() * chars.length + 1);
password += chars.charAt(char);
}
return password;
}
Tuttavia i nostri set di caratteri hanno un ordine prestabilito, quindi occorre creare un metodo che effettui il rimescolamento (shuffle) di tali caratteri.
shuffle(str) {
return str.split('').sort((a, b) => { return Math.random() - 0.5; }).join('');
}
Poichè è l'utente a scegliere quali set di caratteri includere, dobbiamo creare un metodo che tenga traccia dello stato delle checkbox di scelta.
getControlState() {
let state = {
letters: false,
mixed: false,
punctuation: false,
numbers: false
};
state.letters = (this.lettersControl.checked);
state.mixed = (this.mixedControl.checked);
state.punctuation = (this.punctControl.checked);
state.numbers = (this.numberControl.checked);
return state;
}
L'oggetto restituito verrà quindi usato dal metodo che genererà la password.
build(state) {
let characters = '';
if(state.letters) {
characters += this.getCharacters('lowercase');
}
if(state.mixed) {
characters = this.getCharacters('letters');
}
if(state.punctuation) {
characters += this.getCharacters('symbols');
}
if(state.numbers) {
characters += this.getCharacters('numbers');
}
if(characters.length === 0) {
characters = this.getCharacters();
}
return this.shuffle(characters);
}
Ora definiamo il metodo events()
che verrà richiamato nel metodo init()
. In questo metodo associamo gli eventi agli elementi della UI. L'evento principale è il click
sul pulsante di creazione della password.
const self = this;
self.createButton.addEventListener('click', evt => {
evt.preventDefault();
let state = self.getControlState();
self.input.value = self.create(self.build(state), parseInt(self.lengthInput.value, 10));
}, false);
L'evento legge il valore dell'input di tipo range
e lo usa per impostare la lunghezza della password generata.
A questo punto poiché molte azioni dipendono da questo evento, per non duplicare il codice creiamo un metodo che andrà ad innescare l'evento sul pulsante.
triggerEvent(element, name = 'click') {
const evt = new Event(name);
return element.dispatchEvent(evt);
}
Quindi lo usiamo sugli elementi su cui vogliamo innescare la generazione della password.
self.lengthInput.addEventListener('change', () => {
self.triggerEvent(self.createButton, 'click');
}, false);
self.lettersControl.addEventListener('change', () => {
self.triggerEvent(self.createButton, 'click');
}, false);
self.mixedControl.addEventListener('change', () => {
self.triggerEvent(self.createButton, 'click');
}, false);
self.punctControl.addEventListener('change', () => {
self.triggerEvent(self.createButton, 'click');
}, false);
self.numberControl.addEventListener('change', () => {
self.triggerEvent(self.createButton, 'click');
}, false);
L'ultimo controllo da impostare è l'azione di copia della password negli appunti.
self.copyButton.addEventListener('click', evt => {
evt.preventDefault();
self.input.select();
document.execCommand('copy');
self.copyButton.innerText = 'Copied!';
setTimeout(() => {
self.copyButton.innerText = 'Copy Password';
}, 1000);
}, false);
self.triggerEvent(self.createButton, 'click');
L'ultima riga innesca l'evento principale in modo che all'apertura della pagina la password sia già presente come valore del campo di input.
Infine, usiamo la nostra classe in questo modo:
const passwordGenerator = new PasswordGenerator({
input: document.getElementById('password'),
createButton: document.getElementById('create-password'),
copyButton: document.getElementById('copy-password'),
lengthInput: document.getElementById('password-length'),
lettersControl: document.getElementById('password-letters'),
mixedControl: document.getElementById('password-mixed'),
punctControl: document.getElementById('password-punctuation'),
numberControl: document.getElementById('password-numbers')
});