Prática 03 — Login e Fluxo de Requisição em PHP
Esta prática apresenta o fluxo mínimo de autenticação em PHP sem banco de dados:
formulário HTML, envio por POST, validação no servidor,
redirecionamento com header() em caso de erro e exibição da área autenticada
pelo próprio backend.
Objetivo desta prática: compreender o caminho que os dados fazem entre navegador e servidor e perceber que a permissão de acesso depende da validação feita pelo backend. O uso de hash de senha aparece na prática 05, e o controle de sessão será discutido em uma aula subsequente.
Animação do fluxo
O fluxo de comunicação trabalhado nesta prática está demonstrado abaixo.
A animação mostra o navegador pedindo login.php, enviando POST
para autenticar.php e recebendo a resposta do servidor.
Para melhor visualização, abra a
animação em tela cheia.
Conceitos centrais desta prática
requirecarrega um arquivo externo que a página precisa para funcionar.header("Location: ...")faz o PHP redirecionar o navegador para outra página.- Login é uma comparação de informações feita no servidor.
- O array associativo organiza os dados de cada usuário com chaves como
login,senhaeperfil.
Selecione uma prática no menu lateral ou abaixo para construir o sistema passo a passo.
Prática 01 — Separando os arquivos no servidor
Para esta prática, comece criando uma pasta
login dentro de htdocs.
Passo a passo
- Na pasta
htdocs, crie a pastalogin. - Dentro dela, crie os arquvios, ainda em branco,
usuarios.php,login.phpeautenticar.php. - Ligue o Apache no XAMPP.
- Acesse
http://localhost/login/para conferir se a pasta responde pelo servidor.
Estrutura sugerida da pasta
login/
├── usuarios.php
├── login.php
└── autenticar.php
Prática 02 — Criando o vetor hardcoded de usuários
Agora vamos guardar os usuários em um arquivo separado. Isso é importante porque deixa visível qual parte do sistema contém os dados e qual parte contém a regra de login.
Passo a passo
- Abra
usuarios.php. - Copie o vetor de usuários do exemplo abaixo.
- Confira os campos de cada registro:
id,login,senha,nomeeperfil. - Salve o arquivo. Ele será carregado depois pelo
autenticar.php.
Arquivo usuarios.php
<?php
$usuarios = [
[
"id" => 1,
"login" => "ana",
"senha" => "1234",
"nome" => "Ana Souza",
"perfil" => "aluno"
],
[
"id" => 2,
"login" => "bruno",
"senha" => "abcd",
"nome" => "Bruno Lima",
"perfil" => "monitor"
],
[
"id" => 3,
"login" => "carla",
"senha" => "9999",
"nome" => "Carla Rocha",
"perfil" => "professor"
]
];
?>
Cada usuário já aparece como uma entidade com campos nomeados:
id, login, senha, nome e perfil.
Depois, essa mesma ideia pode virar uma linha de banco ou um objeto da classe
Usuario sem mudar o fluxo geral do sistema.
Esse tipo de vetor facilita a leitura do código, porque cada informação é acessada
pelo nome do campo. Em vez de pensar em posições numéricas, o código usa chaves
como $usuario["login"] e $usuario["senha"].
Observação: a senha está em texto puro apenas nesta primeira versão. Isso será trocado em uma etapa posterior.
Prática 03 — Página de login
A página de login contém essencialmente um formulário html. Nesta prática, vamos criá-la.
Passo a passo
- Abra
login.phpe copie o código do formulário abaixo. - Observe os pontos principais:
action="autenticar.php",method="POST"e os camposname="login"ename="senha". - Mantenha o bloco PHP que mostra erro quando existir
$_GET["erro"]. Por conta deste trecho de código (que ficará mais clara nas próximas práticas), este arquivo é .php e não .html. - Salve e acesse
http://localhost/login/login.php.
Este arquivo não é HTML puro. Ele se chama login.php porque contém
um trecho de PHP embutido no final da página. Por isso, precisa ser processado
no servidor pelo interpretador PHP antes de a resposta chegar ao navegador.
Nas próximas práticas ficará claro por que esse trecho PHP existe: ele será usado para mostrar uma mensagem quando o backend redirecionar o usuário de volta após uma tentativa de login inválida.
Arquivo login.php
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<h1>Login do sistema</h1>
<form action="autenticar.php" method="POST">
<label for="login">Login:</label>
<input type="text" name="login" id="login" required>
<br><br>
<label for="senha">Senha:</label>
<input type="password" name="senha" id="senha" required>
<br><br>
<button type="submit">Entrar</button>
</form>
<?php
if (isset($_GET["erro"])) {
echo '<p style="color: red;">Login ou senha inválidos.</p>';
}
?>
</body>
</html>
Pontos de atenção
method="POST"evita mandar usuário e senha na URL.action="autenticar.php"indica qual arquivo receberá os dados.- O bloco com
$_GET["erro"]mostra uma mensagem quando o login falha. - Esta página apenas coleta dados e envia para o backend.
Prática 04 — Autenticando e decidindo entre erro e sucesso
Agora aparece a parte central do backend: receber os dados do formulário,
comparar com os usuários cadastrados e decidir entre erro e sucesso. Se o login
falhar, o PHP redireciona de volta para o formulário. Se passar, o próprio
autenticar.php continua e monta o HTML da área autenticada.
Passo a passo
- Abra
autenticar.phpe copie a versão final abaixo. - Localize quatro partes:
require, leitura doPOST, busca no vetor e decisão final. - Repare que o HTML só começa depois do teste com
$usuarioEncontrado === null. - Exiba nome e perfil usando os dados de
$usuarioEncontrado. - Teste o fluxo completo: formulário → autenticação → resposta de sucesso.
Versão final de autenticar.php
<?php
require "usuarios.php";
$loginDigitado = $_POST["login"];
$senhaDigitada = $_POST["senha"];
$usuarioEncontrado = null;
foreach ($usuarios as $usuario) {
if ($usuario["login"] === $loginDigitado && $usuario["senha"] === $senhaDigitada) {
$usuarioEncontrado = $usuario;
}
}
if ($usuarioEncontrado === null) {
header("Location: login.php?erro=1");
exit;
}
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Painel</title>
</head>
<body>
<h1>Área autenticada</h1>
<p>Bem-vindo, <?php echo htmlspecialchars($usuarioEncontrado["nome"]); ?>.</p>
<p>Seu perfil é: <?php echo htmlspecialchars($usuarioEncontrado["perfil"]); ?>.</p>
<p><a href="login.php">Voltar ao login</a></p>
</body>
</html>
O papel do require
O comando require "usuarios.php"; carrega o arquivo de usuários antes
de o restante do código continuar. Assim, o vetor $usuarios fica
disponível dentro de autenticar.php.
Isso separa responsabilidades: um arquivo guarda os dados e o outro executa a regra de autenticação.
Como o login funciona neste exemplo
Autenticar significa comparar as credenciais recebidas com uma fonte de dados.
O servidor recebe $_POST["login"] e $_POST["senha"],
percorre o vetor de usuários e verifica se existe um registro com os mesmos valores.
Login não é um comando pronto da linguagem. Login é uma regra de comparação feita no servidor. Quando a comparação dá certo, o script continua e pode gerar o HTML da área autenticada.
O operador de identidade (===)
Em PHP, === compara valor e tipo. Já == compara apenas
o valor depois de tentar converter os tipos automaticamente.
No código, $usuarioEncontrado começa como null. Se nenhum
usuário for encontrado, ele continua exatamente null. Por isso usamos
$usuarioEncontrado === null, sem conversões automáticas.
O que este código mostra
header("Location: ...")manda o navegador ir para outra página.exit;encerra a execução quando o login falha.- Se o login falhar, o redirecionamento volta para
login.php. - Se o login der certo, o código passa pelo
ife chega ao HTML de sucesso. - Os dados exibidos vêm do usuário encontrado no vetor pelo backend.
Cuidado importante: header() precisa ser chamado antes de
qualquer saída HTML ou echo. Se a página já tiver enviado conteúdo ao navegador,
o redirecionamento falha.
Prática 05 — Função hash e senha criptografada
Até aqui, o arquivo usuarios.php guardava a senha exatamente como ela era
digitada. Isso ajuda a entender o fluxo inicial, mas não é adequado para uma aplicação real.
Na prática, não armazenamos a senha em claro: armazenamos um hash da senha.
O que é hash
Uma função de hash recebe um texto e gera uma sequência fixa de caracteres. Se a entrada for a mesma, o resultado será o mesmo. Por isso, o servidor consegue comparar o hash da senha digitada com o hash salvo no cadastro.
A ideia importante é: o usuário continua digitando a senha normal no formulário, mas o
backend transforma essa senha com hash("sha256", $senhaDigitada) antes de
comparar com o valor armazenado.
Essa estratégia é importante pois se alguém visualizar o arquivo de usuários (ou so dados no banco de dados), não verá
as senhas originais em claro. Verá apenas os hashes. Nesta prática, vamos usar
sha256 para entender a ideia.
Passo a passo
- Abra
usuarios.phpe troque as senhas em claro pelos hashes abaixo. - Abra
autenticar.phpe calcule o hash da senha recebida pelo formulário. - Compare o login digitado com
$usuario["login"]e o hash calculado com$usuario["senha"]. - Teste usando as senhas originais:
1234,abcde9999.
Arquivo usuarios.php
<?php
$usuarios = [
[
"id" => 1,
"login" => "ana",
"senha" => "03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4",
"nome" => "Ana Souza",
"perfil" => "aluno"
],
[
"id" => 2,
"login" => "bruno",
"senha" => "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589",
"nome" => "Bruno Lima",
"perfil" => "monitor"
],
[
"id" => 3,
"login" => "carla",
"senha" => "888df25ae35772424a560c7152a1de794440e0ea5cfee62828333a456a506e05",
"nome" => "Carla Rocha",
"perfil" => "professor"
]
];
?>
Arquivo autenticar.php
<?php
require "usuarios.php";
$loginDigitado = $_POST["login"];
$senhaDigitada = $_POST["senha"];
$hashSenhaDigitada = hash("sha256", $senhaDigitada);
$usuarioEncontrado = null;
foreach ($usuarios as $usuario) {
if ($usuario["login"] === $loginDigitado && $usuario["senha"] === $hashSenhaDigitada) {
$usuarioEncontrado = $usuario;
}
}
if ($usuarioEncontrado === null) {
header("Location: login.php?erro=1");
exit;
}
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Painel</title>
</head>
<body>
<h1>Área autenticada</h1>
<p>Bem-vindo, <?php echo htmlspecialchars($usuarioEncontrado["nome"]); ?>.</p>
<p>Seu perfil é: <?php echo htmlspecialchars($usuarioEncontrado["perfil"]); ?>.</p>
<p><a href="login.php">Voltar ao login</a></p>
</body>
</html>
Conferindo a mudança
A senha de Ana ainda é 1234, mas o arquivo guarda
03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4.
Quando Ana digita 1234, o PHP calcula o mesmo hash e a comparação passa.
Se a senha digitada for diferente, o hash gerado também será diferente, e o usuário
será redirecionado de volta para login.php?erro=1.
Discussão 01 — Entendendo o limite deste modelo
Neste modelo, não existe uma segunda página protegida. A área autenticada é o trecho
de HTML que aparece no final de autenticar.php, depois da validação.
Regra de leitura do autenticar.php
if ($usuarioEncontrado === null) {
header("Location: login.php?erro=1");
exit;
}
// Se chegou aqui, o backend já validou login e senha.
// O HTML abaixo só será gerado no caso de sucesso.
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Sistema interno</title>
</head>
<body>
<h1>Bem-vindo ao sistema interno</h1>
<p>Olá, <?php echo htmlspecialchars($usuarioEncontrado["nome"]); ?>.</p>
<p>Login: <?php echo htmlspecialchars($usuarioEncontrado["login"]); ?></p>
<p>Perfil: <?php echo htmlspecialchars($usuarioEncontrado["perfil"]); ?></p>
<p>Você está visualizando uma área gerada pelo backend após a autenticação.</p>
<p><a href="login.php">Voltar ao login</a></p>
</body>
</html>
Neste fluxo, o HTML autenticado fica dentro de autenticar.php e só é
executado depois que o backend compara POST com o vetor de usuários.
O navegador envia os dados; o PHP decide se gera ou não gera a resposta de sucesso.
Limite importante: sem sessão, conceito que ainda será trabalhado, o login não fica guardado entre páginas ou entre requisições. Este modelo serve para entender a decisão do backend, não para manter um usuário navegando autenticado por várias telas.
Discussão 02 — Preparando a evolução da aplicação
O sistema já cumpre o objetivo desta prática: mostrar o caminho completo do formulário até a decisão de acesso. A próxima etapa será melhorar esse modelo.
Mapa de substituições para as próximas aulas
Hoje: require "usuarios.php" carrega um vetor hardcoded.
Depois: esse ponto pode virar uma consulta no banco e retornar os usuários.
Versão inicial: a comparação usa $usuario["senha"] === $senhaDigitada.
Evolução imediata: esse ponto passa a comparar o hash salvo com hash("sha256", $senhaDigitada).
Hoje: cada usuário é um array associativo.
Depois: cada usuário pode virar objeto.
Hoje: o HTML autenticado fica no próprio autenticar.php.
Depois: a aplicação pode usar sessão para manter o login entre várias páginas.
Desafio final: adicione o campo turma em cada usuário,
e exiba esse valor no HTML de sucesso gerado por autenticar.php.
Exercício 01 — Adicionando um novo usuário
Este exercício parte do sistema já pronto. O objetivo é alterar apenas o vetor de usuários e observar como isso já modifica o comportamento do login.
Tarefa
Adicione um novo usuário em usuarios.php com os campos
id, login, senha, nome e
perfil. Em seguida, teste o login com esse novo cadastro.
Arquivos envolvidos
usuarios.phppara cadastrar o novo registro.login.phppara testar o formulário com o novo login.autenticar.phppara observar que a lógica não precisa ser alterada.
usuarios.php
<?php
$usuarios = [
[
"id" => 1,
"login" => "ana",
"senha" => "123",
"nome" => "Ana Souza",
"perfil" => "aluno"
],
[
"id" => 2,
"login" => "bruno",
"senha" => "456",
"nome" => "Bruno Lima",
"perfil" => "professor"
],
[
// Novo usuário adicionado ao vetor.
// A lógica de autenticação não muda: o foreach já percorre todos os registros.
"id" => 3,
"login" => "carla",
"senha" => "789",
"nome" => "Carla Mendes",
"perfil" => "aluno"
]
];
?>
Exercício 02 — Exibindo um novo campo
Agora o objetivo é ampliar a estrutura de dados do usuário e acompanhar essa mudança no fluxo até o HTML de sucesso.
Tarefa
Adicione o campo turma em todos os usuários do vetor.
Depois, altere autenticar.php para que esse valor também
seja exibido no HTML gerado em caso de sucesso.
Arquivos envolvidos
usuarios.phppara incluir o novo campo.autenticar.phppara exibir a informação após validar o login.
usuarios.php e autenticar.php
<?php
// usuarios.php
// O campo turma passa a fazer parte de cada usuário.
$usuarios = [
[
"id" => 1,
"login" => "ana",
"senha" => "123",
"nome" => "Ana Souza",
"perfil" => "aluno",
"turma" => "3INFOA"
],
[
"id" => 2,
"login" => "bruno",
"senha" => "456",
"nome" => "Bruno Lima",
"perfil" => "professor",
"turma" => "3INFOB"
]
];
// autenticar.php
// Depois da validação, o novo campo pode ser exibido no HTML de sucesso.
?>
<p>Turma: <?php echo htmlspecialchars($usuarioEncontrado["turma"]); ?></p>
Exercício 03 — Melhorando as mensagens de erro
Neste exercício, o foco é refinar a resposta do sistema quando o login falha. Em vez de uma mensagem genérica, o código deve explicar melhor o motivo do erro.
Tarefa
Altere o sistema para diferenciar pelo menos dois erros:
login inexistente e senha incorreta.
Depois, faça login.php mostrar uma mensagem apropriada para cada caso.
Arquivos envolvidos
autenticar.phppara identificar os diferentes cenários de falha.login.phppara ler o parâmetro de erro e exibir a mensagem correta.
Dicas
- Primeiro procure apenas pelo
logindigitado, sem comparar a senha. - Se nenhum usuário tiver esse login, redirecione com um erro específico, como
erro=login. - Se o login existir, compare a senha desse usuário com a senha digitada.
- Se a senha estiver errada, redirecione com outro erro, como
erro=senha. - Em
login.php, use o valor de$_GET["erro"]para escolher qual mensagem mostrar.
autenticar.php
<?php
require "usuarios.php";
$loginDigitado = $_POST["login"];
$senhaDigitada = $_POST["senha"];
$usuarioEncontrado = null;
foreach ($usuarios as $usuario) {
// Primeiro procuramos apenas pelo login.
if ($usuario["login"] === $loginDigitado) {
$usuarioEncontrado = $usuario;
}
}
if ($usuarioEncontrado === null) {
// O login digitado não existe no vetor.
header("Location: login.php?erro=login");
exit;
}
if ($usuarioEncontrado["senha"] !== $senhaDigitada) {
// O login existe, mas a senha não confere.
header("Location: login.php?erro=senha");
exit;
}
// Se chegou aqui, login e senha estão corretos.
?>
login.php
<?php
if (isset($_GET["erro"]) && $_GET["erro"] === "login") {
echo '<p style="color: red;">Login inexistente.</p>';
} elseif (isset($_GET["erro"]) && $_GET["erro"] === "senha") {
echo '<p style="color: red;">Senha incorreta.</p>';
}
?>
Exercício 04 — Exibindo HTML por perfil
Agora o login deve continuar validando usuário e senha, mas o HTML de sucesso dependerá do perfil encontrado pelo backend.
Tarefa
Modifique autenticar.php para exibir uma área interna diferente
dependendo do campo perfil do usuário autenticado.
Se o perfil for aluno, mostre um HTML com conteúdo da área dos alunos.
Se o perfil for professor, mostre um HTML com conteúdo da área dos professores.
Não use redirecionamento para outras páginas neste exercício.
Arquivos envolvidos
autenticar.phppara validar login e senha e depois escolher qual HTML mostrar.login.phpcontinua sendo usado apenas quando a autenticação falha.
Dicas
- Depois que
$usuarioEncontradodeixar de sernull, verifique$usuarioEncontrado["perfil"]. - Use um
ifpara o perfilalunoe outro para o perfilprofessor. - Dentro de cada
if, feche o PHP com?>, escreva o HTML daquele perfil e volte para o PHP se precisar continuar. - Esse modelo evita criar páginas separadas que precisariam de sessão para serem protegidas corretamente.
autenticar.php
<?php
require "usuarios.php";
$loginDigitado = $_POST["login"];
$senhaDigitada = $_POST["senha"];
$usuarioEncontrado = null;
foreach ($usuarios as $usuario) {
// Primeiro autentica: login e senha precisam existir no backend.
if ($usuario["login"] === $loginDigitado && $usuario["senha"] === $senhaDigitada) {
$usuarioEncontrado = $usuario;
}
}
if ($usuarioEncontrado === null) {
// Se não autenticou, volta para o formulário.
header("Location: login.php?erro=1");
exit;
}
// A partir daqui, o usuário já foi autenticado.
// Agora vem a autorização: qual HTML este perfil pode ver?
if ($usuarioEncontrado["perfil"] === "aluno") {
// Se o perfil for aluno, geramos o HTML da área dos alunos.
?>
<!DOCTYPE html>
<html lang="pt-BR">
<body>
<h1>Área dos alunos</h1>
<p>Bem-vindo, <?php echo htmlspecialchars($usuarioEncontrado["nome"]); ?>.</p>
<p>Aqui ficam materiais, atividades e avisos para estudantes.</p>
</body>
</html>
<?php
}
if ($usuarioEncontrado["perfil"] === "professor") {
// Se o perfil for professor, geramos o HTML da área dos professores.
?>
<!DOCTYPE html>
<html lang="pt-BR">
<body>
<h1>Área dos professores</h1>
<p>Bem-vindo, <?php echo htmlspecialchars($usuarioEncontrado["nome"]); ?>.</p>
<p>Aqui ficam lançamento de notas, turmas e materiais administrativos.</p>
</body>
</html>
<?php
}
// Se o perfil não for reconhecido, exibimos uma mensagem simples.
if ($usuarioEncontrado["perfil"] !== "aluno" && $usuarioEncontrado["perfil"] !== "professor") {
echo "Perfil não reconhecido.";
}
?>
Observação importante: em uma aplicação melhor organizada, o ideal seria
redirecionar o usuário para páginas separadas, como area_aluno.php e
area_professor.php.
if ($usuarioEncontrado["perfil"] === "aluno") {
header("Location: area_aluno.php");
exit;
}
if ($usuarioEncontrado["perfil"] === "professor") {
header("Location: area_professor.php");
exit;
}
O problema é que, se fizermos apenas isso agora, qualquer pessoa poderia abrir
area_aluno.php ou area_professor.php diretamente pela URL,
sem passar pelo login.
Para proteger essas páginas separadas, precisamos de um mecanismo que mantenha no servidor a informação de que aquele usuário já foi autenticado. Esse mecanismo é a sessão, assunto da próxima aula.
Referência rápida
Use esta tabela para revisar os termos centrais da prática.
| Termo | Significado |
|---|---|
| HTTP stateless | Cada requisição é independente. O servidor responde uma requisição por vez. |
| $_POST | Array superglobal que recebe os dados enviados por formulário com método POST. |
| header("Location: ...") | Adiciona um cabeçalho Location à resposta para orientar o navegador a fazer uma nova requisição. |
| exit | Interrompe o script para que nenhum HTML seja gerado depois do redirecionamento. |
| require | Carrega um arquivo obrigatório, como a base de usuários ou funções de apoio. |
| array associativo | Estrutura que guarda dados com chaves nomeadas, como login, senha e perfil. |
| === | Compara valor e tipo. Útil para verificar exatamente se algo ainda é null. |
| autenticação | Processo de comparar os dados enviados pelo formulário com os dados cadastrados no servidor. |
| hash | Resultado gerado por uma função que transforma a senha digitada em uma sequência usada para comparação. |
| hash("sha256", $senha) | Função usada nesta prática para gerar o hash da senha antes de comparar com o valor salvo. |


