No desenvolvimento web moderno, autenticação de usuários é algo bastante comum e serve como mecanismo de segurança para acessos não autorizados a um ambiente onde somente quem tem cadastro pode entrar, como uma área de membros.
Aqui o usuário terá a opção de criar um cadastro preenchendo um formulário, ou seja, se cadastrando. Portanto, o primeiro passo é criar a tabela no banco de dados.
CREATE TABLE users (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Para criar a tabela, abra o PHPMyAdmin, clique no banco de dados test, depois vá até a aba SQL, cole o código acima e clique em Executar.
✅ Dessa forma, a tabela users será criada para armazenar os dados de login.
config.php)Neste passo, vamos criar o arquivo config.php responsável por conectar o sistema ao banco de dados MySQL usando PDO.
<?php
/* Credenciais do banco de dados.
Supondo que você esteja executando o MySQL
com configuração padrão (usuário 'root' sem senha) */
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '');
define('DB_NAME', 'test');
/* Tentativa de conexão com o banco de dados MySQL */
try {
$pdo = new PDO("mysql:host=" . DB_SERVER . ";dbname=" . DB_NAME, DB_USERNAME, DB_PASSWORD);
// Define o modo de erro do PDO para exceção
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
die("ERROR: Não foi possível conectar. " . $e->getMessage());
}
?>
Nota: Substitua as credenciais conforme o seu ambiente local, se necessário.
DB_NAME se o nome do seu banco não for test.root e a senha conforme o seu usuário do MySQL.register.php)Agora vamos criar o arquivo register.php. Ele será responsável por permitir que novos usuários se cadastrem, verificando se o nome de usuário já existe e exibindo mensagens de erro caso algo esteja incorreto.
<?php
// Incluir arquivo de configuração
require_once "config.php";
// Defina variáveis e inicialize com valores vazios
$username = $password = $confirm_password = "";
$username_err = $password_err = $confirm_password_err = "";
// Processando dados do formulário quando o formulário é enviado
if($_SERVER["REQUEST_METHOD"] == "POST"){
// Validar nome de usuário
if(empty(trim($_POST["username"]))){
$username_err = "Por favor coloque um nome de usuário.";
} elseif(!preg_match('/^[a-zA-Z0-9_]+$/', trim($_POST["username"]))){
$username_err = "O nome de usuário pode conter apenas letras, números e sublinhados.";
} else{
// Prepare uma declaração selecionada
$sql = "SELECT id FROM users WHERE username = :username";
if($stmt = $pdo->prepare($sql)){
$stmt->bindParam(":username", $param_username, PDO::PARAM_STR);
$param_username = trim($_POST["username"]);
if($stmt->execute()){
if($stmt->rowCount() == 1){
$username_err = "Este nome de usuário já está em uso.";
} else{
$username = trim($_POST["username"]);
}
} else{
echo "Ops! Algo deu errado. Por favor, tente novamente mais tarde.";
}
unset($stmt);
}
}
// Validar senha
if(empty(trim($_POST["password"]))){
$password_err = "Por favor insira uma senha.";
} elseif(strlen(trim($_POST["password"])) < 6){
$password_err = "A senha deve ter pelo menos 6 caracteres.";
} else{
$password = trim($_POST["password"]);
}
// Validar e confirmar a senha
if(empty(trim($_POST["confirm_password"]))){
$confirm_password_err = "Por favor, confirme a senha.";
} else{
$confirm_password = trim($_POST["confirm_password"]);
if(empty($password_err) && ($password != $confirm_password)){
$confirm_password_err = "A senha não confere.";
}
}
// Inserir no banco de dados se tudo estiver correto
if(empty($username_err) && empty($password_err) && empty($confirm_password_err)){
$sql = "INSERT INTO users (username, password) VALUES (:username, :password)";
if($stmt = $pdo->prepare($sql)){
$stmt->bindParam(":username", $param_username, PDO::PARAM_STR);
$stmt->bindParam(":password", $param_password, PDO::PARAM_STR);
$param_username = $username;
$param_password = password_hash($password, PASSWORD_DEFAULT);
if($stmt->execute()){
header("location: login.php");
} else{
echo "Ops! Algo deu errado. Por favor, tente novamente mais tarde.";
}
unset($stmt);
}
}
unset($pdo);
}
?>
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<title>Cadastro</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<style>
body{ font: 14px sans-serif; }
.wrapper{ width: 360px; padding: 20px; }
</style>
</head>
<body>
<div class="wrapper">
<h2>Cadastro</h2>
<p>Por favor, preencha este formulário para criar uma conta.</p>
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post">
<div class="form-group">
<label>Nome do usuário</label>
<input type="text" name="username" class="form-control <?php echo (!empty($username_err)) ? 'is-invalid' : ''; ?>" value="<?php echo $username; ?>">
<span class="invalid-feedback"><?php echo $username_err; ?></span>
</div>
<div class="form-group">
<label>Senha</label>
<input type="password" name="password" class="form-control <?php echo (!empty($password_err)) ? 'is-invalid' : ''; ?>" value="<?php echo $password; ?>">
<span class="invalid-feedback"><?php echo $password_err; ?></span>
</div>
<div class="form-group">
<label>Confirme a senha</label>
<input type="password" name="confirm_password" class="form-control <?php echo (!empty($confirm_password_err)) ? 'is-invalid' : ''; ?>" value="<?php echo $confirm_password; ?>">
<span class="invalid-feedback"><?php echo $confirm_password_err; ?></span>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Criar Conta">
<input type="reset" class="btn btn-secondary ml-2" value="Apagar Dados">
</div>
<p>Já tem uma conta? <a href="login.php">Entre aqui</a>.</p>
</form>
</div>
</body>
</html>
Após salvar o arquivo, o resultado será semelhante à imagem abaixo:
Como no exemplo acima, usamos a função embutida password_hash() do PHP para gerar um hash seguro da senha digitada pelo usuário.
Isso garante que, mesmo que dois usuários escolham a mesma senha, os hashes armazenados no banco serão diferentes devido ao uso automático de salt.
No momento do login, vamos comparar a senha digitada com o hash armazenado usando password_verify(), como veremos no próximo passo.
Também utilizamos o framework Bootstrap para estilizar o formulário de forma rápida e elegante.
Dica: “Salting” é uma técnica que adiciona uma string aleatória à senha antes de realizar o hash, tornando os ataques de dicionário muito mais difíceis.
login.php)Aqui criaremos o formulário de login, onde o usuário irá inserir suas credenciais cadastradas (nome de usuário e senha). Ao enviar os dados, o sistema verifica as informações com as armazenadas no banco de dados. Se as credenciais estiverem corretas, o usuário será autenticado e redirecionado para a área restrita. Caso contrário, mensagens de erro serão exibidas.
Vamos criar o arquivo login.php dentro da pasta raiz do projeto.
<?php
// Inicialize a sessão
session_start();
// Verifique se o usuário já está logado, em caso afirmativo, redirecione-o para a página de boas-vindas
if(isset($_SESSION["loggedin"]) && $_SESSION["loggedin"] === true){
header("location: welcome.php");
exit;
}
// Incluir arquivo de configuração
require_once "config.php";
// Defina variáveis e inicialize com valores vazios
$username = $password = "";
$username_err = $password_err = $login_err = "";
// Processando dados do formulário quando o formulário é enviado
if($_SERVER["REQUEST_METHOD"] == "POST"){
// Verifique se o nome de usuário está vazio
if(empty(trim($_POST["username"]))){
$username_err = "Por favor, insira o nome de usuário.";
} else{
$username = trim($_POST["username"]);
}
// Verifique se a senha está vazia
if(empty(trim($_POST["password"]))){
$password_err = "Por favor, insira sua senha.";
} else{
$password = trim($_POST["password"]);
}
// Validar credenciais
if(empty($username_err) && empty($password_err)){
// Prepare uma declaração selecionada
$sql = "SELECT id, username, password FROM users WHERE username = :username";
if($stmt = $pdo->prepare($sql)){
// Vincule as variáveis à instrução preparada como parâmetros
$stmt->bindParam(":username", $param_username, PDO::PARAM_STR);
// Definir parâmetros
$param_username = trim($_POST["username"]);
// Tente executar a declaração preparada
if($stmt->execute()){
// Verifique se o nome de usuário existe, se sim, verifique a senha
if($stmt->rowCount() == 1){
if($row = $stmt->fetch()){
$id = $row["id"];
$username = $row["username"];
$hashed_password = $row["password"];
if(password_verify($password, $hashed_password)){
// A senha está correta, então inicie uma nova sessão
session_start();
// Armazene dados em variáveis de sessão
$_SESSION["loggedin"] = true;
$_SESSION["id"] = $id;
$_SESSION["username"] = $username;
// Redirecionar o usuário para a página de boas-vindas
header("location: welcome.php");
} else{
// A senha não é válida, exibe uma mensagem de erro genérica
$login_err = "Nome de usuário ou senha inválidos.";
}
}
} else{
// O nome de usuário não existe, exibe uma mensagem de erro genérica
$login_err = "Nome de usuário ou senha inválidos.";
}
} else{
echo "Ops! Algo deu errado. Por favor, tente novamente mais tarde.";
}
// Fechar declaração
unset($stmt);
}
}
// Fechar conexão
unset($pdo);
}
?>
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<title>Login</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<style>
body{ font: 14px sans-serif; }
.wrapper{ width: 360px; padding: 20px; }
</style>
</head>
<body>
<div class="wrapper">
<h2>Login</h2>
<p>Por favor, preencha os campos para fazer o login.</p>
<?php
if(!empty($login_err)){
echo '<div class="alert alert-danger">' . $login_err . '</div>';
}
?>
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post">
<div class="form-group">
<label>Nome do usuário</label>
<input type="text" name="username" class="form-control <?php echo (!empty($username_err)) ? 'is-invalid' : ''; ?>" value="<?php echo $username; ?>">
<span class="invalid-feedback"><?php echo $username_err; ?></span>
</div>
<div class="form-group">
<label>Senha</label>
<input type="password" name="password" class="form-control <?php echo (!empty($password_err)) ? 'is-invalid' : ''; ?>">
<span class="invalid-feedback"><?php echo $password_err; ?></span>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Entrar">
</div>
<p>Não tem uma conta? <a href="register.php">Inscreva-se agora</a>.</p>
</form>
</div>
</body>
</html>
Após inserir o código do arquivo login.php, o resultado será igual ao da imagem abaixo:
Nesta etapa, o login é validado com segurança utilizando a função password_verify(),
que compara a senha digitada com o hash armazenado no banco.
O uso de $_SESSION garante que apenas usuários autenticados possam acessar páginas protegidas como welcome.php.
welcome.php)Nesta etapa, criaremos a página para onde o usuário será redirecionado após ter suas credenciais validadas com sucesso no sistema. Aqui ele verá uma saudação personalizada com seu nome de usuário e poderá redefinir a senha ou sair da conta.
<?php
// Inicialize a sessão
session_start();
// Verifique se o usuário está logado, se não, redirecione-o para uma página de login
if(!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true){
header("location: login.php");
exit;
}
?>
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<title>Bem vindo</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<style>
body{ font: 14px sans-serif; text-align: center; }
</style>
</head>
<body>
<h1 class="my-5">Oi, <b><?php echo htmlspecialchars($_SESSION["username"]); ?></b>. Bem vindo ao nosso site.</h1>
<p>
<a href="reset-password.php" class="btn btn-warning">Redefina sua senha</a>
<a href="logout.php" class="btn btn-danger ml-3">Sair da conta</a>
</p>
</body>
</html>
Após a inserção do código acima, a página terá a seguinte aparência:
O PHP verifica se a variável de sessão $_SESSION["loggedin"] está definida e verdadeira;
caso contrário, o usuário é redirecionado de volta para login.php.
Esse controle impede o acesso direto à página sem login.
logout.php)Agora criaremos o script responsável por encerrar a sessão do usuário. Quando ele clicar no botão Sair da conta, o sistema destruirá a sessão e redirecionará automaticamente para a tela de login.
<?php
// Inicialize a sessão
session_start();
// Remova todas as variáveis de sessão
$_SESSION = array();
// Destrua a sessão.
session_destroy();
// Redirecionar para a página de login
header("location: login.php");
exit;
?>
Com isso, o ciclo completo do sistema de autenticação está concluído — do registro ao login e logout.
Na próxima etapa, você pode implementar o arquivo reset-password.php para permitir que o usuário redefina sua senha de forma segura.
reset-password.php)Agora vamos criar nossa página e script para redefinir a senha do usuário logado. Essa funcionalidade é importante para permitir que o usuário altere sua senha com segurança sem precisar criar outra conta.
Crie um novo arquivo chamado reset-password.php na pasta raiz do seu projeto e adicione o código abaixo:
<?php
// Inicialize a sessão
session_start();
// Verifique se o usuário está logado, caso contrário, redirecione para a página de login
if(!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true){
header("location: login.php");
exit;
}
// Incluir arquivo de configuração
require_once "config.php";
// Defina variáveis e inicialize com valores vazios
$new_password = $confirm_password = "";
$new_password_err = $confirm_password_err = "";
// Processando dados do formulário quando o formulário é enviado
if($_SERVER["REQUEST_METHOD"] == "POST"){
// Validar nova senha
if(empty(trim($_POST["new_password"]))){
$new_password_err = "Por favor insira a nova senha.";
} elseif(strlen(trim($_POST["new_password"])) < 6){
$new_password_err = "A senha deve ter pelo menos 6 caracteres.";
} else{
$new_password = trim($_POST["new_password"]);
}
// Validar e confirmar a senha
if(empty(trim($_POST["confirm_password"]))){
$confirm_password_err = "Por favor, confirme a senha.";
} else{
$confirm_password = trim($_POST["confirm_password"]);
if(empty($new_password_err) && ($new_password != $confirm_password)){
$confirm_password_err = "A senha não confere.";
}
}
// Verifique os erros de entrada antes de atualizar o banco de dados
if(empty($new_password_err) && empty($confirm_password_err)){
// Prepare uma declaração de atualização
$sql = "UPDATE users SET password = :password WHERE id = :id";
if($stmt = $pdo->prepare($sql)){
// Vincule as variáveis à instrução preparada como parâmetros
$stmt->bindParam(":password", $param_password, PDO::PARAM_STR);
$stmt->bindParam(":id", $param_id, PDO::PARAM_INT);
// Definir parâmetros
$param_password = password_hash($new_password, PASSWORD_DEFAULT);
$param_id = $_SESSION["id"];
// Tente executar a declaração preparada
if($stmt->execute()){
// Senha atualizada com sucesso. Destrua a sessão e redirecione para a página de login
session_destroy();
header("location: login.php");
exit();
} else{
echo "Ops! Algo deu errado. Por favor, tente novamente mais tarde.";
}
// Fechar declaração
unset($stmt);
}
}
// Fechar conexão
unset($pdo);
}
?>
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<title>Redefinir senha</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<style>
body{ font: 14px sans-serif; }
.wrapper{ width: 360px; padding: 20px; margin: 0 auto; }
</style>
</head>
<body>
<div class="wrapper">
<h2>Redefinir senha</h2>
<p>Por favor, preencha este formulário para redefinir sua senha.</p>
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post">
<div class="form-group">
<label>Nova senha</label>
<input type="password" name="new_password" class="form-control <?php echo (!empty($new_password_err)) ? 'is-invalid' : ''; ?>" value="<?php echo $new_password; ?>">
<span class="invalid-feedback"><?php echo $new_password_err; ?></span>
</div>
<div class="form-group">
<label>Confirme a senha</label>
<input type="password" name="confirm_password" class="form-control <?php echo (!empty($confirm_password_err)) ? 'is-invalid' : ''; ?>">
<span class="invalid-feedback"><?php echo $confirm_password_err; ?></span>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Redefinir">
<a class="btn btn-link ml-2" href="welcome.php">Cancelar</a>
</div>
</form>
</div>
</body>
</html>
Após adicionar o código acima, a página ficará assim:
Com isso, o usuário logado pode redefinir sua senha com segurança. O sistema exige uma senha de pelo menos 6 caracteres e verifica se a confirmação confere. Ao atualizar com sucesso, a sessão é encerrada e o usuário é redirecionado para a página de login.
---Agora temos um sistema de login funcional e completo, composto por cadastro, autenticação, controle de sessão, logout e redefinição de senha. Esse é um ótimo ponto de partida para projetos reais, pois já inclui boas práticas, como:
password_hash() e password_verify() para segurança das senhas.Naturalmente, há espaço para melhorias — como autenticação em duas etapas, proteção contra CSRF e XSS, recuperação de senha via e-mail e adoção de uma arquitetura MVC. Mas o essencial está aqui: um sistema seguro, limpo e funcional.