Jag har redan skrivit ett blogginlägg om att skapa ett komplett användarregistrering och inloggningssystem med PHP och MySQL men jag inkluderade inte e-postverifiering.
Denna handledning är lite som en uppdatering av den tidigare handledningen. Utöver att kunna registrera sig, logga in och logga ut från sitt konto kommer en användare även att skickas ett verifieringsmail till sin e-postadress med en länk där de kan klicka på och få sin e-postadress verifierad.
På de flesta applikationer du ska bygga är det viktigt att lägga till en e-postverifieringsfunktion av många anledningar:du kanske vill skicka ett e-postmeddelande till användaren senare och vill vara säker på att de kommer att se det; eller så kan användaren glömma sitt lösenord och behöva återställa det, och för att göra detta måste vi skicka en länk för återställning av lösenord via e-post; det finns många andra skäl men du förstår poängen.
I det här systemet vi bygger idag kommer användare som inte har verifierat sin e-post inte att kunna utföra vissa åtgärder (jag kommer bara att använda en enkel demonstration som att visa en knapp. Endast verifierade användare kommer att kunna se den knappen).
Börja med att skapa ett nytt PHP-projekt med namnet verify-user och skapa två filer i den här mappen:signup.php och login.php.
signup.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>
Det är bara en enkel HTML/CSS-fil. Det enda värt att notera är att vi använder Bootstrap 4 CSS-ramverket för att styla vår sida. Du kan använda valfritt annat stylingramverk eller skriva din egen CSS om du vill.
Omedelbart efter Bootstrap CSS inkluderar vi en main.css-fil för anpassad styling. Låt oss skapa den filen nu. Skapa en fil som heter main.css.
i programmets rotmappmain.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;
}
På den första raden i den här filen importerar vi och använder några Google Fonts för att få våra typsnitt att se vackrare ut.
Gå nu till filen login.php och gör en liknande sak.
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>
I din webbläsare, gå till http://localhost/cwa/verify-user/signup.php du kommer att se ett vackert registreringsformulär (samma för inloggning). Ignorera felen i inmatningsfälten, vi fixar det snart.
För nu, låt oss ställa in databasen. Skapa en databas som heter verify-user och i den här databasen skapar du en användartabell med attribut enligt följande:
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`)
)
Inget ovanligt förutom kanske token och verifierade fält, som jag kommer att förklara om ett ögonblick.
Nu börjar vi med själva registreringslogiken. Jag brukar vilja hänvisa till den logiska delen av min applikation som kontrollerna och det är vad jag kommer att göra här. I projektets rotmapp, skapa en mapp som heter controllers och inuti controllers, skapa en fil som heter 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";
}
}
}
Om du har följt mina tidigare tutorials bör ingenting i den här filen vara nytt för dig. Men för nybörjarnas skull ska jag förklara lite.
Det första är att vi startar sessionen med session_start() eftersom vi kommer att behöva lagra inloggad användarinformation i sessionen. Efter att ha startat sessionen, initierar vi $username och $email-variablerna som vi använder i våra formulär, och även $errors-arrayen som kommer att hålla våra formulärvalideringsfel.
Därefter ansluter vi till databasen. De följande två if-satserna som följer är respektive kod som exekveras när användaren klickar på registrerings- eller inloggningsknapparna. Vid registrering kontrollerar vi att alla obligatoriska fält har fyllts i korrekt och först därefter fortsätter vi att spara användaren i databasen. Vi genererar också en token (en unik, slumpmässig sträng) och sparar den med användaren som ett attribut. Detta kommer att användas för att verifiera användarens e-post. Mer om det senare.
Eftersom vår authController.php-fil är ansvarig för registrering och inloggning, måste vi inkludera den högst upp på sidorna signup.php och login.php eftersom det är dit formulärdata skickas till. Som så:
signup.php och login.php (högst upp):
<?php include 'controllers/authController.php' ?>
Om det finns några felmeddelanden i $errors-arrayen måste vi visa dem i formuläret. För att göra det, lägg till detta if-uttalande i ditt formulär direkt under formulärets titel för både registrerings- och inloggningssidor.
<!-- 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;?>
Om det inte finns några fel fortsätter vårt skript att spara användare i databasen. Efter att ha sparat användaren i databasen loggar vi in dem direkt. I vårt fall innebär att logga in en användare att lagra deras data i sessionen och det är vad vi precis gjorde.
Vid det här laget kan du redan registrera dig och till och med logga in en användare. Men efter att ha loggat in kommer du att omdirigeras till sidan index.php som inte finns. Vi skapar det snart.
I authController.php lagrar vi meddelande- och typvariabler i sessionen för att visas så snart användaren har loggat in. meddelande är den faktiska texten i meddelandet medan typen är Bootstrap-stylingklassen som kommer att formatera meddelandet med lämpligt färger beroende på typvärdet.
Detta meddelande visas efter att användaren har loggat in och det visas på filen index.php. Låt oss skapa den filen nu i rotmappen i vårt projekt.
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>
Denna sida är tänkt att endast vara tillgänglig för inloggade användare. Det är därför vi i den övre delen av filen omdirigerar användaren till inloggningssidan om de inte är inloggade. Någonstans i mitten av sidan visar vi meddelandet. Efter att ha visat meddelandet avaktiverar vi meddelandet och skriver variabler eftersom vi inte vill att det ska finnas kvar på sidan även efter att användaren har uppdaterat sidan.
Slutligen, i mitten av sidan, utför vi en kontroll för att se om den inloggade användaren har verifierat sin e-postadress eller inte. Kom ihåg att vi la till den verifierade variabeln i sessionen när vi loggade in användaren. Om användaren har verifierats visar vi meddelandet "Jag är verifierad!!!" knappen så att de kan se. Om de inte är verifierade berättar vi för dem om verifieringslänken vi skickade till deras e-postadress och vi ber dem att klicka på den länken för att verifiera sin e-postadress.
Verifiera e-post
I authController.php-filen använde vi en kommentar för att ange var vi skulle skicka verifieringsmeddelandet till användaren genom att ringa sendVerificationEmail(). Gå till filen authController.php och avkommentera funktionsanropet så här:
// TO DO: send verification email to user
sendVerificationEmail($email, $token);
Vi kommer att definiera den här funktionen i en annan fil och inkludera den filen i authController.php. Skapa en fil med namnet sendEmails.php i controllers-mappen.
Innan vi lägger till någon kod i den här filen, låt mig säga lite om PHP SwiftMailer, det populära biblioteket för att skicka e-postmeddelanden i PHP som vi kommer att använda i det här projektet för att skicka e-postmeddelanden från localhost.
SwiftMailer är ett populärt funktionsrikt bibliotek för att skicka e-postmeddelanden i PHP-applikationer.
För att använda Swiftmailer måste du först installera Composer. När du har installerat composer, öppna din terminal eller kommandorad och navigera till rotmappen för projektet och kör följande kommando för att lägga till Swift Mailer-biblioteket med alla dess filer till vårt projekt:
composer require "swiftmailer/swiftmailer:^6.0"
Detta skapar en leverantörsmapp i roten av vår applikation som innehåller all kod (klasser) som krävs för att skicka ett e-postmeddelande och det skapar också en composer.json-fil i roten av applikationen som ser ut så här:
{
"require": {
"swiftmailer/swiftmailer": "^6.0"
}
}
Öppna nu filen sendEmails.php som vi skapade tidigare och låt oss skriva funktionen 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;
}
}
Den första satsen kräver filen autoload.php i den här filen. Denna autoload.php-fil kommer automatiskt att inkludera alla klasser från Swift Mailer-biblioteket i leverantörsmappen som vi använder i den här filen.
Vi använder Gmail i det här exemplet. Så du kan ersätta SENDER_EMAIL och SENDER_PASSWORD med din Gmail-adress och lösenord som du vill använda som avsändarens e-postadress. (Mottagarens e-postadress är den som skickats in via formuläret).
Normalt, för att skicka ett e-postmeddelande till någon, måste du logga in på ditt Gmail-konto innan du skriver e-postmeddelandet och skickar. Det är samma typ som Swift Mailer-biblioteket gör. Så när mottagaren ($userEmail) tar emot e-postmeddelandet är det din Gmail-adress (SENDER_EMAIL) som de kommer att se som det skickande e-postmeddelandet.
Nu anropar vi funktionen sendVerificationEmail() i vår authController.php-fil men vi definierade funktionen i filen sendEmails.php. Låt oss inkludera filen sendEmails.php i vår authController.php för att göra denna funktion tillgänglig i filen. Överst på authController.php, strax före session_start(), lägg till följande rad:
require_once 'sendEmails.php';
Det är allt vi behöver, mina herrar (och mina damer) för att skicka ett e-postmeddelande till vår användare med en e-postverifieringslänk. Men vi arbetar med localhost och Gmail kommer att blockera alla inloggningsförsök från Swift Mailer som körs på localhost.
Skicka e-post från localhost
Om du vill att detta ska köras på localhost måste du konfigurera ditt Gmail-konto för att acceptera inloggning från mindre säkra appar. Naturligtvis kan detta utgöra en viss sårbarhet på ditt Gmail-konto men du kan bara göra det under den korta tid som du behöver för att testa den här applikationen på localhost. Efter testet kan du ångra inställningen på ditt Gmail-konto. När din applikation har lagrats på internet kommer du att arbeta. Vi gör det bara för att vi är på localhost.
Du kan vara ännu mer försiktig och skapa ett annat Gmail-konto endast för sådana ändamål som dessa.
Så logga in på Gmail i din webbläsare, gå till https://myaccount.google.com/security#connectedapps och ändra värdet "Tillåt mindre säkra appar" till PÅ.
Du kan stänga av detta när du är klar med att testa projektet på localhost.
Med detta kommer du att kunna skicka ett e-postmeddelande från localhost med verifieringslänk när användaren registrerar sig. Titta nu på sendVerificationEmail()-metoden igen och du kommer att märka att i brödtexten i e-postmeddelandet vi skickar till användaren, har tokenen vi genererade för den specifika användaren (tokenen är unik) satts som en parameter på länken så att när användaren klickar på länken i e-postmeddelandet kommer de att dirigeras till vår applikation på en sida som heter verify_email.php med den token på webbadressen. Så här:
<a href="http://localhost/cwa/verify-user/verify_email.php?token=0a150966418fa3a694bcb3ab8fcacd2063a096accc0ee33c3e8c863538ee825c0b52f2e1535d0e1377558c378ba5fc3106eb">Verify Email!</a>
Så vi kan få denna token i vår verify_email.php så här (lugna ner, vi skapar denna verify_email.php snart):
$token = $_GET['token'];
Vi kan nu använda denna token för att hämta användaren som har just denna token (kom ihåg att token är unik) och om vi får den användaren uppdaterar vi deras post och ändrar det verifierade attributet till true i databasen. Sedan kan vi stolt säga att vi har verifierat användarens e-postadress.
Låt oss skapa denna verify_email.php-fil i rotmappen för vårt projekt:
verify_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!";
}
Observera att att ställa in värdet för verifierat värde till 1 är detsamma som att ställa in det till sant eftersom i MySQL databastyp tolkas Boolean som tinyint.
Nu när användaren klickar på länken i sin e-post och den tar dem till den här sidan, uppdaterar den användarens verifierade status till sant, loggar in dem och omdirigerar dem till sidan index.php. På indexsidan, efter att ha verifierat användaren, kommer du att märka att varningsmeddelandet som uppmanar användaren att verifiera sin e-postadress nu är borta och i dess ställe har vi "Jag är verifierad!!!" knapp som endast är synlig för verifierade användare.
En allra sista sak, på index.php-sidan efter att ha loggat in användare, finns det en utloggningslänk som pekar på en logout.php-fil som är tänkt att logga ut användaren. Låt oss skapa den filen i roten av vår applikation:
logout.php:
<?php
session_destroy();
unset($_SESSION['id']);
unset($_SESSION['username']);
unset($_SESSION['email']);
unset($_SESSION['verify']);
header("location: login.php");
Så förutom att kunna registrera sig, verifiera e-post, logga in, kan användaren nu också logga ut.
Slutsats
Så det är ungefär det med användarregistrering och e-postverifiering. Om du har några kommentarer, frågor eller uppmuntrande ord, vänligen lämna dem i kommentaren nedan. Och snälla, kom ihåg att dela det här inlägget eller rekommendera den här sidan till dina vänner om du tyckte att den var till hjälp. Det uppmuntrar mig mycket!
Ha en bra dag!