CRUD - Login de Usuários
🏛️ Arquitetura
A aplicação seguirá o padrão Monolítico Servidor-Cliente, onde o Spring Boot atua como o centro de tudo:
- Modelo (Model): Classes Java que representam os dados (ex: a entidade
User). - Visão (View): Ficheiros HTML com Thymeleaf que renderizam a interface para o utilizador.
- Controlador (Controller): Classes Java que recebem os pedidos do utilizador, processam a lógica de negócio e devolvem a visão apropriada.
- Segurança (Security): O Spring Security irá proteger as páginas e gerir o fluxo de autenticação.
🚀 Fase 1: Criação e Configuração do Projeto
1. Gerar o Projeto no Spring Initializr
Aceda a start.spring.io e preencha:
- Project:
Maven - Language:
Java - Spring Boot: Última versão (ex: 3.3.1)
- Project Metadata:
- Group:
br.com.curso - Artifact:
loginusuarios - Java:
21
- Group:
- Dependencies:
Spring WebThymeleafSpring Data JPAH2 DatabaseSpring Security
2. Configurar a Base de Dados (application.properties)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbcmem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update🧩 Fase 2: Modelagem dos Dados
Entidade User
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nome;
private String email;
private String senha;
// getters e setters
}Repositório UserRepository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}🔐 Fase 3: Configuração da Segurança
Classe SecurityConfig
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/registar", "/css/**").permitAll()
.anyRequest().authenticated())
.formLogin(form -> form
.loginPage("/login").permitAll()
.defaultSuccessUrl("/usuarios", true))
.logout(logout -> logout.logoutSuccessUrl("/login?logout"));
return http.build();
}
}Serviço CustomUserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired private UserRepository userRepository;
@Autowired private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("Utilizador não encontrado: " + email));
return org.springframework.security.core.userdetails.User
.withUsername(user.getEmail())
.password(user.getSenha())
.authorities(new ArrayList<>())
.build();
}
public void save(User user) {
user.setSenha(passwordEncoder.encode(user.getSenha()));
userRepository.save(user);
}
}🖥️ Fase 4: Controladores
AuthController.java
@Controller
public class AuthController {
@Autowired private CustomUserDetailsService userService;
@GetMapping("/login")
public String showLoginPage() { return "login"; }
@GetMapping("/registar")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new User());
return "registar";
}
@PostMapping("/registar")
public String processRegistration(@ModelAttribute User user) {
userService.save(user);
return "redirect:/login?success";
}
}UserController.java
@Controller
public class UserController {
@Autowired private UserRepository userRepository;
@GetMapping("/usuarios")
public String listUsers(Model model) {
model.addAttribute("users", userRepository.findAll());
return "usuarios";
}
}🎨 Fase 5: Páginas Thymeleaf
login.html
<form th:action="@{/login}" method="post">
<input type="email" name="username" required>
<input type="password" name="password" required>
<button type="submit">Entrar</button>
</form>
<a th:href="@{/registar}">Registe-se aqui</a>registar.html
<form th:action="@{/registar}" th:object="${user}" method="post">
<input type="text" th:field="*{nome}" required>
<input type="email" th:field="*{email}" required>
<input type="password" th:field="*{senha}" required>
<button type="submit">Registar</button>
</form>
<a th:href="@{/login}">Já tem conta? Login</a>usuarios.html
<table>
<tr th:each="user : ${users}">
<td th:text="${user.id}"></td>
<td th:text="${user.nome}"></td>
<td th:text="${user.email}"></td>
</tr>
</table>
<form th:action="@{/logout}" method="post">
<button type="submit">Sair</button>
</form>🏃 Fase 6: Executar e Testar
- Execute
LoginusuariosApplication.java. - Registe em http://localhost:8080/registar.
- Faça login em http://localhost:8080/login.
- Veja utilizadores em http://localhost:8080/usuarios.
- (Opcional) Explore http://localhost:8080/h2-console.
Atualizar as páginas Thymeleaf (login.html, registar.html e usuarios.html) para ficarem prontas com Bootstrap 5, mantendo a estrutura bonita e responsiva.
## 🎨 Fase 5: Páginas Thymeleaf com Bootstrap
### `login.html`
```html
<!DOCTYPE html>
<html lang="pt" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container">
<div class="row justify-content-center align-items-center" style="height:100vh;">
<div class="col-md-5">
<div class="card shadow">
<div class="card-body">
<h3 class="card-title text-center mb-4">Aceder ao Sistema</h3>
<!-- Mensagens -->
<div th:if="${param.error}" class="alert alert-danger">Email ou senha inválidos.</div>
<div th:if="${param.logout}" class="alert alert-info">Sessão terminada com sucesso.</div>
<div th:if="${param.success}" class="alert alert-success">Registo efetuado com sucesso!</div>
<form th:action="@{/login}" method="post">
<div class="mb-3">
<label for="username" class="form-label">Email</label>
<input type="email" id="username" name="username" class="form-control" required autofocus>
</div>
<div class="mb-3">
<label for="password" class="form-label">Senha</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Entrar</button>
</div>
</form>
<div class="text-center mt-3">
<a th:href="@{/registar}">Não tem conta? Registe-se aqui</a>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>registar.html
<!DOCTYPE html>
<html lang="pt" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Registar</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container">
<div class="row justify-content-center align-items-center" style="height:100vh;">
<div class="col-md-5">
<div class="card shadow">
<div class="card-body">
<h3 class="card-title text-center mb-4">Criar Conta</h3>
<form th:action="@{/registar}" th:object="${user}" method="post">
<div class="mb-3">
<label for="nome" class="form-label">Nome</label>
<input type="text" id="nome" th:field="*{nome}" class="form-control" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" id="email" th:field="*{email}" class="form-control" required>
</div>
<div class="mb-3">
<label for="senha" class="form-label">Senha</label>
<input type="password" id="senha" th:field="*{senha}" class="form-control" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-success">Registar</button>
</div>
</form>
<div class="text-center mt-3">
<a th:href="@{/login}">Já tem conta? Faça login</a>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>usuarios.html
<!DOCTYPE html>
<html lang="pt" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Lista de Utilizadores</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">Painel</a>
<form class="d-flex" th:action="@{/logout}" method="post">
<button class="btn btn-outline-light" type="submit">Sair</button>
</form>
</div>
</nav>
<div class="container mt-4">
<h1 class="mb-4">Utilizadores Registados</h1>
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nome</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.id}"></td>
<td th:text="${user.nome}"></td>
<td th:text="${user.email}"></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
👉 Agora todos os HTMLs estão com **Bootstrap 5 integrado**, com **layout responsivo, cartões, alertas e tabelas estilizadas**.