O Redirect Lab é um caso de estudo focado na construção de um gerenciador de links e redirects completo. A ideia central vai além de encurtar URLs — o objetivo é também coletar métricas de acesso (dispositivos, navegadores, IPs, referers, cliques por dia) para alimentar dashboards analíticas que geram insights reais sobre o comportamento dos acessos.
O desenvolvimento começou com a criação de páginas SSR (Server-Side Rendering) usando Thymeleaf, com fins educacionais para aprender a fundo essa abordagem, enquanto simultaneamente as rotas da API REST são expostas para consumo de um front-end externo. Atualmente, todas as telas e features estão implementadas — com exceção da dashboard de métricas por link, que já possui toda a funcionalidade no backend pronta para ser consumida.
- Reaprender autenticação em Java — tanto com sessões web (Session + CSRF) quanto com JWT para APIs stateless, entendendo a fundo como o Spring Security 6 funciona por baixo dos panos.
- Pensar em arquitetura dentro de um MVC da maneira mais modular possível, isolando as camadas de negócio para que módulos inteiros (como o de
Users) possam ser reutilizados em outros projetos com mínimas alterações. - Familiaridade com Java 21 e stack moderna — este projeto me deu confiança para adotar a versão LTS 21 em produção, migrando de versões anteriores com segurança.
Painel principal com visão geral do sistema, total de URLs, status e informações da stack.
Autenticação web com suporte a sessão, CSRF e opção de "Lembrar de mim".
Criação, edição e controle de redirects. Inclui filtros avançados por slug, status e data.
Gestão de usuários do sistema com filtros, permissões e ações administrativas.
Visualização analítica por link com total de cliques, IPs únicos, cliques por dia, distribuição por sistema operacional, navegador, top referers e top IPs.
| Tecnologia | Versão | Descrição |
|---|---|---|
| Java | 21 | Linguagem principal (Records, Pattern Matching, etc.) |
| Spring Boot | 3.5.8 | Framework base com auto-configuração |
| Spring Security | 6.x | Autenticação e autorização (Session + JWT) |
| Spring Data JPA | - | ORM e persistência |
| Flyway | - | Migrations e versionamento de schema |
| Lombok | - | Redução de boilerplate |
| JJWT | 0.12.5 | Geração e validação de JWT |
| Redis | - | Cache em produção |
| SpringDoc OpenAPI | 2.7.0 | Documentação Swagger |
| Tecnologia | Versão | Descrição |
|---|---|---|
| Thymeleaf | 3.x | Template engine server-side |
| Thymeleaf Extras Spring Security 6 | - | Diretivas sec:authorize nos templates |
| DaisyUI | 4.12.14 | Componentes UI (tema Winter) |
| Tailwind CSS | CDN | Utility-first CSS |
| Lucide Icons | latest | Ícones SVG |
| Tecnologia | Versão | Descrição |
|---|---|---|
| PostgreSQL | 16 | Banco de dados relacional |
| Flyway | - | Controle de versão do schema |
| Redis | - | Cache distribuído (opcional em dev) |
| Spring Dotenv | 4.0.0 | Gerenciamento de variáveis .env |
| Docker / Podman | - | Containerização |
O projeto segue uma arquitetura em camadas com baixo acoplamento entre módulos, inspirada em Clean Architecture e DDD simplificado. A filosofia principal é que a camada Domain seja completamente reutilizável em outros projetos.
┌─────────────────────────────────────────────────────────────┐
│ API Layer │
│ Controllers (REST + Web), DTOs, Mappers │
│ Comunicação com o mundo externo │
└─────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────────┐
│ Domain Layer │
│ Entities, Repositories, Services │
│ Regras de negócio, tokens, validações │
│ * Módulo reutilizável em outros projetos │
└─────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────────┐
│ Core Layer │
│ SecurityConfig, Filters, Exception Handlers │
│ Configurações globais e respostas padrão │
└─────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────────┐
│ Infrastructure Layer │
│ Properties, Schedulers, Integrações externas │
└─────────────────────────────────────────────────────────────┘
src/main/java/space/bielsolososdev/rdl/
├── api/ # Camada de API
│ ├── controller/
│ │ ├── rest/ # Controllers REST (JWT Auth)
│ │ └── web/ # Controllers Web (Session Auth)
│ ├── mapper/ # Conversores DTO <-> Entity
│ └── model/ # DTOs e Records
│
├── core/ # Camada Core
│ ├── config/ # Configurações gerais
│ ├── exception/ # Exceções customizadas + Global Handler
│ ├── security/ # Spring Security Config + JWT Filter
│ └── utils/ # Utilitários
│
├── domain/ # Camada de Domínio
│ ├── url/ # Módulo de URLs + Métricas
│ │ ├── model/ # Entities (UrlRedirect, UrlMetrics)
│ │ ├── repository/ # JPA Repositories
│ │ └── service/ # UrlRedirectService, UrlMetricsService
│ │
│ └── users/ # Módulo de Usuários
│ ├── model/ # Entity User, Role
│ ├── repository/ # UserRepository
│ └── service/ # UserService, AuthService, JWT, etc.
│
├── infrastructure/ # Camada de Infraestrutura
│ ├── RdlProperties.java # @ConfigurationProperties
│ └── scheduler/ # Jobs agendados
│
└── RdlApplication.java # Entry point
Modularidade: A camada
domain/usersfoi projetada para ser copiada e reutilizada em outros projetos — basta ajustar o package e configurar as properties de JWT.
O RDL implementa dois sistemas de autenticação em paralelo, atendendo diferentes cenários:
Usada nas páginas renderizadas com Thymeleaf:
- Spring Security Form Login com sessões HTTP
- CSRF Protection integrado em todos os formulários
- Remember-me com cookie persistente
- Thymeleaf Security Dialect (
sec:authorize,sec:authentication) para controle de visibilidade no template
Usada para integrações REST e consumo externo:
- Access Token JWT com curta duração
- Refresh Token com longa duração e single-use (rotação automática)
- Stateless — sem sessão no servidor
- Filtro JWT aplicado exclusivamente em rotas
/api/**
# Login - retorna tokens
POST /api/auth/login
{ "username": "user", "password": "password" }
# Resposta
{ "accessToken": "eyJhbGc...", "refreshToken": "uuid-token", "expiresIn": 900000 }
# Renovar token
POST /api/auth/refresh
{ "refreshToken": "uuid-token" }| Rota | Método | Auth | Descrição |
|---|---|---|---|
/ |
GET | Pública | Home page |
/login |
GET/POST | Pública | Login web |
/urls/** |
* | Session | Gerenciamento de URLs |
/profile/** |
* | Session | Perfil do usuário |
/admin/** |
* | Session (ADMIN) | Painel administrativo |
/api/auth/** |
POST | Pública | Endpoints de autenticação |
/api/** |
* | JWT | API REST protegida |
/r/{slug} |
GET | Pública | Redirecionamento |
O sistema possui um módulo completo de coleta e visualização de métricas por URL, incluindo:
- Total de cliques e IPs únicos
- Último acesso registrado
- Cliques por dia em gráfico de barras
- Distribuição por sistema operacional e navegador em gráficos de rosca
- Top Referers — de onde vêm os acessos
- Top IPs — endereços mais frequentes
- Validação de ownership — cada usuário só visualiza métricas dos seus próprios redirects
- Java 21+
- PostgreSQL 16+
- Maven 3.8+
- Redis (opcional em dev)
# Clone o repositório
git clone https://github.com/bielsolosos/Redirect-Lab.git
cd Redirect-Lab
# Crie o banco de dados
psql -U postgres -c "CREATE DATABASE \"rdl-db\";"
# Configure as variáveis de ambiente
cp .env.example .env
# Edite o .env com suas configurações
# Execute
./mvnw spring-boot:run# Database
DB_URL=jdbc:postgresql://localhost:5432/rdl-db
DB_USERNAME=postgres
DB_PASSWORD=sua_senha
# JWT
JWT_SECRET=sua-chave-secreta-256-bits-minimo
JWT_EXPIRATION=900000
JWT_REFRESH_EXPIRATION=604800000
# Redis (opcional em dev)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
# App
APP_NAME=Redirect Lab
SHOW_SQL=false
REGISTRATION_ENABLED=falseEste projeto está licenciado sob a MIT License — o que significa que você é livre para usar, copiar, modificar, mesclar, publicar, distribuir, sublicenciar e/ou vender cópias do software, desde que a nota de copyright e a permissão sejam incluídas em todas as cópias.
Veja o arquivo LICENSE para o texto completo da licença.
Desenvolvido por @bielsolosos
Se este projeto foi útil, considere dar uma estrela!





