Ho già scritto un post sul blog sulla creazione di una registrazione utente completa e sistema di accesso utilizzando PHP e MySQL, ma non ho incluso la verifica e-mail.
Questo tutorial è un po' come un aggiornamento del tutorial precedente. Oltre a potersi registrare, accedere e disconnettersi dal proprio account, a un utente verrà inviata anche un'e-mail di verifica al proprio indirizzo e-mail con un collegamento su cui può fare clic e verificare il proprio indirizzo e-mail.
Sulla maggior parte delle applicazioni che creerai, è importante aggiungere una funzione di verifica e-mail per molte ragioni:potresti voler inviare un'e-mail in un secondo momento all'utente e vuoi assicurarti che la vedano; oppure l'utente potrebbe dimenticare la propria password e aver bisogno di reimpostarla e, per fare ciò, dovremo inviargli via e-mail un collegamento per reimpostare la password; ci sono molte altre ragioni, ma hai capito.
In questo sistema che stiamo costruendo oggi, gli utenti che non hanno verificato la loro email non saranno in grado di eseguire determinate azioni (mi limiterò a utilizzare una semplice dimostrazione come la visualizzazione di un pulsante. Solo gli utenti verificati potranno vedere quel pulsante).
Per iniziare, crea un nuovo progetto PHP denominato utente-verifica e in questa cartella crea due file:signup.php e login.php.
iscrizione.php:
<!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">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" />
<link rel="stylesheet" href="main.css">
<title>User verification system PHP</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-4 offset-md-4 form-wrapper auth">
<h3 class="text-center form-title">Register</h3>
<form action="signup.php" method="post">
<div class="form-group">
<label>Username</label>
<input type="text" name="username" class="form-control form-control-lg" value="<?php echo $username; ?>">
</div>
<div class="form-group">
<label>Email</label>
<input type="text" name="email" class="form-control form-control-lg" value="<?php echo $email; ?>">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" class="form-control form-control-lg">
</div>
<div class="form-group">
<label>Password Confirm</label>
<input type="password" name="passwordConf" class="form-control form-control-lg">
</div>
<div class="form-group">
<button type="submit" name="signup-btn" class="btn btn-lg btn-block">Sign Up</button>
</div>
</form>
<p>Already have an account? <a href="login.php">Login</a></p>
</div>
</div>
</div>
</body>
</html>
È solo un semplice file HTML/CSS. L'unica cosa degna di nota è che stiamo usando il framework CSS Bootstrap 4 per dare uno stile alla nostra pagina. Puoi utilizzare qualsiasi altro framework di stile a tua scelta o scrivere il tuo CSS se lo desideri.
Immediatamente dopo il CSS Bootstrap, stiamo includendo un file main.css per uno stile personalizzato. Creiamo quel file ora. Nella cartella principale dell'applicazione, crea un file chiamato main.css.
main.css:
@import url('https://fonts.googleapis.com/css?family=Lora');
li { list-style-type: none; }
.form-wrapper {
margin: 50px auto 50px;
font-family: 'Lora', serif;
font-size: 1.09em;
}
.form-wrapper.login { margin-top: 120px; }
.form-wrapper p { font-size: .8em; text-align: center; }
.form-control:focus { box-shadow: none; }
.form-wrapper {
border: 1px solid #80CED7;
border-radius: 5px;
padding: 25px 15px 0px 15px;
}
.form-wrapper.auth .form-title { color: #007EA7; }
.home-wrapper button,
.form-wrapper.auth button {
background: #007EA7;
color: white;
}
.home-wrapper {
margin-top: 150px;
border-radius: 5px;
padding: 10px;
border: 1px solid #80CED7;
}
Nella prima riga di questo file importiamo e utilizziamo alcuni caratteri Google per rendere i nostri caratteri più belli.
Ora vai al file login.php e fai una cosa simile.
login.php:
<!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">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" />
<link rel="stylesheet" href="main.css">
<title>User verification system PHP - Login</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-4 offset-md-4 form-wrapper auth login">
<h3 class="text-center form-title">Login</h3>
<form action="login.php" method="post">
<div class="form-group">
<label>Username or Email</label>
<input type="text" name="username" class="form-control form-control-lg" value="<?php echo $username; ?>">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" class="form-control form-control-lg">
</div>
<div class="form-group">
<button type="submit" name="login-btn" class="btn btn-lg btn-block">Login</button>
</div>
</form>
<p>Don't yet have an account? <a href="signup.php">Sign up</a></p>
</div>
</div>
</div>
</body>
</html>
Sul tuo browser, vai su http://localhost/cwa/verify-user/signup.php vedrai un bellissimo modulo di registrazione (lo stesso per il login). Ignora gli errori nei campi di input, lo risolveremo presto.
Per ora, impostiamo il database. Crea un database chiamato verifica-utente e in questo database crea una tabella utenti con gli attributi seguenti:
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(100) NOT NULL,
`email` varchar(100) NOT NULL,
`verified` tinyint(1) NOT NULL DEFAULT '0',
`token` varchar(255) DEFAULT NULL,
`password` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
)
Niente di insolito tranne, forse, il token e i campi verificati, che spiegherò tra poco.
Ora iniziamo con la logica di registrazione effettiva. Di solito mi piace fare riferimento alla parte logica della mia applicazione come controller ed è quello che farò qui. Nella cartella principale del progetto, crea una cartella chiamata controllers e dentro controllers, crea un file chiamato authController.php.
controllers/authController.php:
<?php
session_start();
$username = "";
$email = "";
$errors = [];
$conn = new mysqli('localhost', 'root', '', 'verify-user');
// SIGN UP USER
if (isset($_POST['signup-btn'])) {
if (empty($_POST['username'])) {
$errors['username'] = 'Username required';
}
if (empty($_POST['email'])) {
$errors['email'] = 'Email required';
}
if (empty($_POST['password'])) {
$errors['password'] = 'Password required';
}
if (isset($_POST['password']) && $_POST['password'] !== $_POST['passwordConf']) {
$errors['passwordConf'] = 'The two passwords do not match';
}
$username = $_POST['username'];
$email = $_POST['email'];
$token = bin2hex(random_bytes(50)); // generate unique token
$password = password_hash($_POST['password'], PASSWORD_DEFAULT); //encrypt password
// Check if email already exists
$sql = "SELECT * FROM users WHERE email='$email' LIMIT 1";
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
$errors['email'] = "Email already exists";
}
if (count($errors) === 0) {
$query = "INSERT INTO users SET username=?, email=?, token=?, password=?";
$stmt = $conn->prepare($query);
$stmt->bind_param('ssss', $username, $email, $token, $password);
$result = $stmt->execute();
if ($result) {
$user_id = $stmt->insert_id;
$stmt->close();
// TO DO: send verification email to user
// sendVerificationEmail($email, $token);
$_SESSION['id'] = $user_id;
$_SESSION['username'] = $username;
$_SESSION['email'] = $email;
$_SESSION['verified'] = false;
$_SESSION['message'] = 'You are logged in!';
$_SESSION['type'] = 'alert-success';
header('location: index.php');
} else {
$_SESSION['error_msg'] = "Database error: Could not register user";
}
}
}
// LOGIN
if (isset($_POST['login-btn'])) {
if (empty($_POST['username'])) {
$errors['username'] = 'Username or email required';
}
if (empty($_POST['password'])) {
$errors['password'] = 'Password required';
}
$username = $_POST['username'];
$password = $_POST['password'];
if (count($errors) === 0) {
$query = "SELECT * FROM users WHERE username=? OR email=? LIMIT 1";
$stmt = $conn->prepare($query);
$stmt->bind_param('ss', $username, $password);
if ($stmt->execute()) {
$result = $stmt->get_result();
$user = $result->fetch_assoc();
if (password_verify($password, $user['password'])) { // if password matches
$stmt->close();
$_SESSION['id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['email'] = $user['email'];
$_SESSION['verified'] = $user['verified'];
$_SESSION['message'] = 'You are logged in!';
$_SESSION['type'] = 'alert-success';
header('location: index.php');
exit(0);
} else { // if password does not match
$errors['login_fail'] = "Wrong username / password";
}
} else {
$_SESSION['message'] = "Database error. Login failed!";
$_SESSION['type'] = "alert-danger";
}
}
}
Se hai seguito i miei tutorial precedenti, allora nulla in questo file dovrebbe essere nuovo per te. Ma per il bene dei principianti, spiegherò qualcosa.
La prima cosa è che stiamo avviando la sessione usando session_start() poiché dovremo memorizzare le informazioni dell'utente connesso nella sessione. Dopo aver avviato la sessione, stiamo inizializzando le variabili $username e $email che stiamo usando nei nostri moduli, e anche l'array $errors che conterrà i nostri errori di convalida del modulo.
Successivamente, ci colleghiamo al database. Le successive due istruzioni if che seguono sono rispettivamente il codice che viene eseguito quando l'utente fa clic sui pulsanti di registrazione o di accesso. In caso di registrazione, controlliamo che tutti i campi obbligatori siano stati compilati correttamente e solo allora procediamo al salvataggio dell'utente nel database. Stiamo anche generando un token (una stringa casuale univoca) e salvandolo con l'utente come attributo. Questo verrà utilizzato per verificare l'e-mail dell'utente. Ne parleremo più avanti.
Poiché il nostro file authController.php è responsabile della registrazione e dell'accesso, dobbiamo includerlo nella parte superiore delle pagine signup.php e login.php perché è lì che vengono inviati i dati del modulo. Così:
signup.php e login.php (in cima):
<?php include 'controllers/authController.php' ?>
Se sono presenti messaggi di errore nell'array $errors, è necessario visualizzarli nel modulo. Per fare ciò, aggiungi questa dichiarazione if all'interno del tuo modulo direttamente sotto il titolo del modulo sia per la pagina di registrazione che per quella di accesso.
<!-- form title -->
<h3 class="text-center form-title">Register</h3> <!-- or Login -->
<?php if (count($errors) > 0): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $error): ?>
<li>
<?php echo $error; ?>
</li>
<?php endforeach;?>
</div>
<?php endif;?>
Se non ci sono errori, il nostro script procederà a salvare l'utente nel database. Dopo aver salvato l'utente nel database, lo accediamo immediatamente. Nel nostro caso, l'accesso di un utente significa memorizzare i suoi dati nella sessione ed è quello che abbiamo appena fatto.
A questo punto, puoi già registrarti e persino accedere a un utente. Ma dopo aver effettuato l'accesso, verrai reindirizzato alla pagina index.php che non esiste. Lo creeremo presto.
In authController.php, memorizziamo il messaggio e le variabili di tipo nella sessione da visualizzare non appena l'utente ha effettuato l'accesso. message è il testo effettivo del messaggio mentre il tipo è la classe di stile Bootstrap che formatterà il messaggio con l'appropriata colori a seconda del valore del tipo.
Questo messaggio viene visualizzato dopo che l'utente ha effettuato l'accesso e viene visualizzato nel file index.php. Creiamo quel file ora nella cartella principale del nostro progetto.
index.php:
<?php include 'controllers/authController.php'?>
<?php
// redirect user to login page if they're not logged in
if (empty($_SESSION['id'])) {
header('location: login.php');
}
?>
<!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">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" />
<link rel="stylesheet" href="main.css">
<title>User verification system PHP</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-4 offset-md-4 home-wrapper">
<!-- Display messages -->
<?php if (isset($_SESSION['message'])): ?>
<div class="alert <?php echo $_SESSION['type'] ?>">
<?php
echo $_SESSION['message'];
unset($_SESSION['message']);
unset($_SESSION['type']);
?>
</div>
<?php endif;?>
<h4>Welcome, <?php echo $_SESSION['username']; ?></h4>
<a href="logout.php" style="color: red">Logout</a>
<?php if (!$_SESSION['verified']): ?>
<div class="alert alert-warning alert-dismissible fade show" role="alert">
You need to verify your email address!
Sign into your email account and click
on the verification link we just emailed you
at
<strong><?php echo $_SESSION['email']; ?></strong>
</div>
<?php else: ?>
<button class="btn btn-lg btn-primary btn-block">I'm verified!!!</button>
<?php endif;?>
</div>
</div>
</div>
</body>
</html>
Questa pagina è pensata per essere accessibile solo agli utenti che hanno effettuato l'accesso. Ecco perché nella sezione superiore del file, stiamo reindirizzando l'utente alla pagina di accesso se non ha effettuato l'accesso. Da qualche parte al centro della pagina, stiamo visualizzando il messaggio. Dopo aver visualizzato il messaggio, disattiviamo il messaggio e digitiamo le variabili perché non vogliamo che rimanga nella pagina anche dopo che l'utente ha aggiornato la pagina.
Infine, al centro della pagina, eseguiamo un controllo per vedere se l'utente che ha effettuato l'accesso ha verificato o meno il proprio indirizzo email. Ricorda che abbiamo aggiunto la variabile verificata nella sessione quando abbiamo effettuato l'accesso all'utente. Se l'utente è stato verificato, visualizziamo il messaggio "Sono verificato!!!" pulsante per farli vedere. Se non sono verificati, comunichiamo loro il link di verifica che abbiamo inviato al loro indirizzo e-mail e chiediamo loro di fare clic su quel link per verificare la loro e-mail.
Verifica email
Nel file authController.php, abbiamo utilizzato un commento per indicare dove avremmo inviato l'email di verifica all'utente chiamando sendVerificationEmail(). Vai al file authController.php e decommenta la chiamata alla funzione in questo modo:
// TO DO: send verification email to user
sendVerificationEmail($email, $token);
Definiremo questa funzione in un altro file e includeremo quel file all'interno di authController.php. All'interno della cartella controllers, crea un file chiamato sendEmails.php.
Prima di aggiungere qualsiasi codice in questo file, vorrei dire qualcosa su PHP SwiftMailer, la popolare libreria per l'invio di e-mail in PHP che utilizzeremo in questo progetto per inviare e-mail da localhost.
SwiftMailer è una popolare libreria ricca di funzionalità per l'invio di e-mail nelle applicazioni PHP.
Per utilizzare Swiftmailer, devi prima installare Composer. Dopo aver installato Composer, apri il terminale o la riga di comando e vai alla cartella principale del progetto ed esegui il comando seguente per aggiungere la libreria Swift Mailer con tutti i suoi file al nostro progetto:
composer require "swiftmailer/swiftmailer:^6.0"
Questo crea una cartella del fornitore nella radice della nostra applicazione contenente tutto il codice (classi) richiesto per l'invio di un'e-mail e crea anche un file composer.json nella radice dell'applicazione che assomiglia a questo:
{
"require": {
"swiftmailer/swiftmailer": "^6.0"
}
}
Ora apriamo il file sendEmails.php che abbiamo creato in precedenza e scriviamo la funzione sendVerificationEmail():
<?php
require_once './vendor/autoload.php';
// Create the Transport
$transport = (new Swift_SmtpTransport('smtp.gmail.com', 465, 'ssl'))
->setUsername(SENDER_EMAIL)
->setPassword(SENDER_PASSWORD);
// Create the Mailer using your created Transport
$mailer = new Swift_Mailer($transport);
function sendVerificationEmail($userEmail, $token)
{
global $mailer;
$body = '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test mail</title>
<style>
.wrapper {
padding: 20px;
color: #444;
font-size: 1.3em;
}
a {
background: #592f80;
text-decoration: none;
padding: 8px 15px;
border-radius: 5px;
color: #fff;
}
</style>
</head>
<body>
<div class="wrapper">
<p>Thank you for signing up on our site. Please click on the link below to verify your account:.</p>
<a href="http://localhost/cwa/verify-user/verify_email.php?token=' . $token . '">Verify Email!</a>
</div>
</body>
</html>';
// Create a message
$message = (new Swift_Message('Verify your email'))
->setFrom(SENDER_EMAIL)
->setTo($userEmail)
->setBody($body, 'text/html');
// Send the message
$result = $mailer->send($message);
if ($result > 0) {
return true;
} else {
return false;
}
}
La prima istruzione richiede il file autoload.php in questo file. Questo file autoload.php includerà automaticamente tutte le classi della libreria Swift Mailer nella cartella del fornitore che utilizziamo in questo file.
Stiamo usando Gmail in questo esempio. Quindi puoi sostituire SENDER_EMAIL e SENDER_PASSWORD con il tuo indirizzo Gmail e la password che desideri utilizzare come indirizzo email del mittente. (L'indirizzo email del destinatario è quello inviato tramite il modulo).
Normalmente, per inviare un'e-mail a qualcuno, devi accedere al tuo account Gmail prima di comporre il messaggio e inviarlo. È lo stesso tipo di operazione che fa la libreria Swift Mailer. Quindi, quando il destinatario ($userEmail) riceve l'e-mail, sarà il tuo indirizzo Gmail (SENDER_EMAIL) che vedrà come l'e-mail di invio.
Ora stiamo chiamando la funzione sendVerificationEmail() nel nostro file authController.php ma abbiamo definito la funzione all'interno del file sendEmails.php. Includiamo il file sendEmails.php all'interno del nostro authController.php per rendere disponibile questa funzione nel file. Nella parte superiore di authController.php, appena prima di session_start(), aggiungi la seguente riga:
require_once 'sendEmails.php';
Questo è tutto ciò di cui abbiamo bisogno, signori (e signore) per inviare un'e-mail al nostro utente con un collegamento di verifica dell'e-mail. Ma stiamo lavorando su localhost e Gmail bloccherà qualsiasi tentativo di accesso da parte di Swift Mailer in esecuzione su localhost.
Invio di email da localhost
Se vuoi che venga eseguito su localhost, dovrai configurare il tuo account Gmail per accettare l'accesso da app meno sicure. Ovviamente, questo può rappresentare una vulnerabilità sul tuo account Gmail, ma puoi farlo solo per il breve tempo necessario per testare questa applicazione su localhost. Dopo il test, puoi annullare l'impostazione sul tuo account Gmail. Una volta che la tua applicazione è stata ospitata su Internet, lavorerai. Lo stiamo facendo solo perché siamo su localhost.
Puoi essere ancora più cauto e creare un altro account Gmail solo per scopi come questi.
Quindi accedi a Gmail sul tuo browser, vai a https://myaccount.google.com/security#connectedapps e imposta il valore "Consenti app meno sicure" su ATTIVO.
Puoi disattivarlo una volta terminato il test del progetto su localhost.
Con questo, sarai in grado di inviare un'e-mail da localhost con link di verifica una volta che l'utente si è registrato. Ora guarda di nuovo il metodo sendVerificationEmail() e noterai che nel corpo dell'email che stiamo inviando all'utente, il token che abbiamo generato per quel particolare utente (il token è univoco) è stato impostato come parametro sul link in modo che quando l'utente fa clic sul collegamento nell'e-mail, verrà indirizzato alla nostra applicazione su una pagina denominata verify_email.php con quel token nell'URL. In questo modo:
<a href="http://localhost/cwa/verify-user/verify_email.php?token=0a150966418fa3a694bcb3ab8fcacd2063a096accc0ee33c3e8c863538ee825c0b52f2e1535d0e1377558c378ba5fc3106eb">Verify Email!</a>
Quindi possiamo ottenere questo token nel nostro Verify_email.php in questo modo (calmati, creeremo presto questo Verify_email.php):
$token = $_GET['token'];
Ora possiamo usare questo token per recuperare l'utente che ha questo particolare token (ricorda che il token è univoco) e se otteniamo quell'utente, aggiorniamo il suo record cambiando l'attributo verificato in true nel database. Quindi, possiamo affermare con orgoglio di aver verificato l'indirizzo email di quell'utente.
Creiamo questo file di verifica_email.php nella cartella principale del nostro progetto:
verificare_email.php:
<?php
session_start();
$conn = new mysqli('localhost', 'root', '', 'verify-user');
if (isset($_GET['token'])) {
$token = $_GET['token'];
$sql = "SELECT * FROM users WHERE token='$token' LIMIT 1";
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
$user = mysqli_fetch_assoc($result);
$query = "UPDATE users SET verified=1 WHERE token='$token'";
if (mysqli_query($conn, $query)) {
$_SESSION['id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['email'] = $user['email'];
$_SESSION['verified'] = true;
$_SESSION['message'] = "Your email address has been verified successfully";
$_SESSION['type'] = 'alert-success';
header('location: index.php');
exit(0);
}
} else {
echo "User not found!";
}
} else {
echo "No token provided!";
}
Tieni presente che impostare il valore del valore verificato su 1 equivale a impostarlo su true poiché nel database MySQL il tipo Boolean viene interpretato come tinyint.
Ora, quando l'utente fa clic sul collegamento nella propria e-mail e lo porta a questa pagina, aggiorna lo stato verificato dell'utente su true, lo accede e lo reindirizza alla pagina index.php. Nella pagina dell'indice, dopo aver verificato l'utente, noterai che il messaggio di avviso che consiglia all'utente di verificare il proprio indirizzo email è ora scomparso e al suo posto abbiamo il messaggio "Sono verificato!!!" pulsante visibile solo agli utenti verificati.
Un'ultima cosa, sulla pagina index.php dopo aver effettuato il login dell'utente, c'è un collegamento di logout che punta a un file logout.php che dovrebbe disconnettere l'utente. Creiamo quel file nella radice della nostra applicazione:
logout.php:
<?php
session_destroy();
unset($_SESSION['id']);
unset($_SESSION['username']);
unset($_SESSION['email']);
unset($_SESSION['verify']);
header("location: login.php");
Quindi, oltre a essere in grado di registrarsi, verificare e-mail, accedere, ora l'utente può anche disconnettersi.
Conclusione
Quindi questo è tutto con la registrazione dell'utente e la verifica e-mail. Se hai commenti, domande o parole di incoraggiamento, lasciali nel commento qui sotto. E per favore, ricordati di condividere questo post o di consigliare questo sito ai tuoi amici se lo hai trovato utile. Mi incoraggia molto!
Buona giornata!