API REST completa com autenticação JWT, CRUD de clientes e documentação Swagger. Desenvolvida com Spring Boot 3 (Java 21) + Maven + H2, preparada para desenvolvimento com DevContainer.
A aplicação está rodando em: http://localhost:8080
Acesse o Swagger UI para testar os endpoints interativamente:
- Swagger UI: http://localhost:8080/docs
- OpenAPI JSON: http://localhost:8080/api-docs
Para visualizar o banco de dados:
- URL: http://localhost:8080/h2-console
- JDBC URL:
jdbc:h2:file:./data/demo - Username:
sa - Password: (deixe em branco)
curl -X POST http://localhost:8080/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "João Silva",
"email": "joao@example.com",
"password": "senha123"
}'Resposta de Sucesso (201 Created):
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "7a3e2f1b-9c8d-4e5f-a6b7-c8d9e0f1a2b3",
"tokenType": "Bearer"
}curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "joao@example.com",
"password": "senha123"
}'Resposta de Sucesso (200 OK):
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "7a3e2f1b-9c8d-4e5f-a6b7-c8d9e0f1a2b3",
"tokenType": "Bearer"
}curl -X POST http://localhost:8080/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "7a3e2f1b-9c8d-4e5f-a6b7-c8d9e0f1a2b3"
}'curl -X POST http://localhost:8080/api/auth/logout \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "7a3e2f1b-9c8d-4e5f-a6b7-c8d9e0f1a2b3"
}'Importante: Todos os endpoints de clientes requerem o token JWT no header Authorization: Bearer {token}
curl -X POST http://localhost:8080/api/clientes \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \
-d '{
"nome": "Maria Santos",
"cpf": "123.456.789-00",
"endereco": {
"logradouro": "Rua das Flores, 123",
"bairro": "Centro",
"cidade": "São Paulo",
"estado": "SP",
"cep": "01234567"
}
}'Resposta de Sucesso (201 Created):
{
"id": 1,
"nome": "Maria Santos",
"cpf": "123.456.789-00",
"endereco": {
"logradouro": "Rua das Flores, 123",
"bairro": "Centro",
"cidade": "São Paulo",
"estado": "SP",
"cep": "01234-567"
},
"createdAt": "2025-11-25T20:00:00",
"updatedAt": "2025-11-25T20:00:00"
}curl -X GET http://localhost:8080/api/clientes \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."curl -X GET http://localhost:8080/api/clientes/1 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."curl -X GET "http://localhost:8080/api/clientes/cpf/12345678900" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."curl -X PUT http://localhost:8080/api/clientes/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \
-d '{
"nome": "Maria Santos Silva",
"cpf": "123.456.789-00",
"endereco": {
"logradouro": "Av. Paulista, 1000",
"bairro": "Bela Vista",
"cidade": "São Paulo",
"estado": "SP",
"cep": "01310100"
}
}'curl -X DELETE http://localhost:8080/api/clientes/1 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."- Formato aceito:
123.456.789-00ou12345678900 - Valida dígitos verificadores
- Rejeita CPFs inválidos (ex:
111.111.111-11) - Verifica unicidade no banco de dados
- nome: obrigatório, entre 3 e 100 caracteres
- cpf: obrigatório, formato e dígitos válidos, único
- endereco.logradouro: obrigatório
- endereco.bairro: obrigatório
- endereco.cidade: obrigatório
- endereco.estado: obrigatório, 2 caracteres (ex: SP, RJ)
- endereco.cep: obrigatório, 8 dígitos
- email: formato válido
- password: mínimo 6 caracteres
- name: obrigatório para registro
# 1. Registrar usuário
RESPONSE=$(curl -s -X POST http://localhost:8080/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "Admin User",
"email": "admin@example.com",
"password": "admin123"
}')
# 2. Extrair token
TOKEN=$(echo $RESPONSE | grep -o '"accessToken":"[^"]*' | cut -d'"' -f4)
# 3. Criar cliente
curl -X POST http://localhost:8080/api/clientes \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"nome": "Pedro Costa",
"cpf": "98765432100",
"endereco": {
"logradouro": "Rua A, 100",
"bairro": "Jardim",
"cidade": "Rio de Janeiro",
"estado": "RJ",
"cep": "20000000"
}
}'# 1. Fazer login
RESPONSE=$(curl -s -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "admin@example.com",
"password": "admin123"
}')
# 2. Extrair token
TOKEN=$(echo $RESPONSE | grep -o '"accessToken":"[^"]*' | cut -d'"' -f4)
# 3. Listar clientes
curl -X GET http://localhost:8080/api/clientes \
-H "Authorization: Bearer $TOKEN"{
"timestamp": "2025-11-25T20:00:00",
"status": 400,
"error": "Bad Request",
"message": "CPF inválido",
"path": "/api/clientes"
}{
"timestamp": "2025-11-25T20:00:00",
"status": 401,
"error": "Unauthorized",
"message": "Credenciais inválidas",
"path": "/api/auth/login"
}{
"timestamp": "2025-11-25T20:00:00",
"status": 403,
"error": "Forbidden",
"message": "Acesso negado",
"path": "/api/clientes"
}{
"timestamp": "2025-11-25T20:00:00",
"status": 404,
"error": "Not Found",
"message": "Cliente não encontrado",
"path": "/api/clientes/999"
}A aplicação está configurada com Spring DevTools para hot reload automático:
- Altere qualquer arquivo
.javano diretóriosrc/ - A aplicação recompila e reinicia automaticamente
- O token JWT continua válido após o reload
src/main/java/com/example/demo/
├── config/
│ └── OpenApiConfig.java # Configuração Swagger
├── controller/
│ ├── AuthController.java # Endpoints de autenticação
│ └── ClienteController.java # CRUD de clientes
├── dto/
│ ├── auth/ # DTOs de autenticação
│ └── cliente/ # DTOs de cliente
├── exception/
│ ├── ErrorResponse.java # Resposta de erro padronizada
│ └── GlobalExceptionHandler.java # Tratamento global de exceções
├── model/
│ ├── Cliente.java # Entidade Cliente
│ ├── Endereco.java # Endereço embarcado
│ ├── RefreshToken.java # Token de refresh
│ └── User.java # Usuário (autenticação)
├── repository/
│ ├── ClienteRepository.java
│ ├── RefreshTokenRepository.java
│ └── UserRepository.java
├── security/
│ ├── JwtAuthenticationFilter.java # Filtro JWT
│ ├── JwtTokenProvider.java # Geração/validação tokens
│ └── SecurityConfig.java # Configuração Spring Security
├── service/
│ ├── AuthService.java # Lógica de autenticação
│ ├── ClienteService.java # Lógica de negócio Cliente
│ ├── CustomUserDetailsService.java
│ └── RefreshTokenService.java
├── validation/
│ ├── CPF.java # Anotação de validação
│ └── CPFValidator.java # Lógica de validação CPF
└── DemoApplication.java
- Token JWT: Válido por 1 minuto (configurável em
application.properties) - Refresh Token: Válido por 5 minutos
- Banco de Dados: Persiste em arquivo
./data/demo.mv.db - Formato CPF: Aceita com ou sem pontuação
- Formato CEP: Armazenado sem hífen, retornado com formatação
- Segurança: Senhas são criptografadas com BCrypt
- Spring Boot 3.3.13
- Spring Security 6.3.10
- Spring Data JPA
- JWT (JJWT 0.12.5)
- H2 Database
- Bean Validation
- Swagger/OpenAPI 3.0
- Lombok
- Maven
- Abra o projeto no VS Code
- Clique em "Reopen in Container" quando solicitado
- Execute:
mvn spring-boot:run
docker-compose upmvn spring-boot:runA aplicação estará disponível em http://localhost:8080 com hot reload ativo.
Este projeto é um exemplo para fins educacionais.