Refinamento Sprint 2 - Gestão de usuários
Visão Geral
Stack Tecnológica
Backend (Spring Boot)
- Framework: Spring Boot 3.x
- Persistência: Spring Data JPA + PostgreSQL/MySQL
- Validação: Bean Validation (Hibernate Validator)
- Segurança: Spring Security + JWT
- Documentação: OpenAPI 3 (Swagger)
- Logs: Logback/SLF4J
Frontend (Next.js)
- Framework: Next.js 14+ (App Router)
- UI Components: React + TypeScript
- Styling: Tailwind CSS ou Material-UI
- State Management: React Query/TanStack Query
- Forms: React Hook Form + Zod validation
- HTTP Client: Axios ou Fetch API
Estrutura de Dados
Entidades Principais
- User: Entidade base para colaboradores e PDMs
- UserType: ENUM (COLLABORATOR, PDM)
- UserStatus: ENUM (ACTIVE, INACTIVE)
Relacionamentos
- User → User: Relacionamento self-referencing (PDM → Colaborador)
APIs REST
Endpoints Principais
GET /api/users- Lista paginada de usuáriosPOST /api/users- Criação de usuárioGET /api/users/{id}- Detalhes do usuárioPUT /api/users/{id}- Atualização de usuárioDELETE /api/users/{id}- Desativação de usuárioGET /api/users/pdms- Lista de PDMs disponíveis
Padrões
- DTOs: Request/Response separados das entidades
- Validation: Validação de negócio no service layer
- Exception Handling: Global exception handler
- Pagination: Spring Data Pageable
Componentes Frontend
Páginas/Rotas
/admin/users- Lista de usuários/admin/users/new- Cadastro de usuário/admin/users/[id]/edit- Edição de usuário
Componentes Principais
- UserList: Tabela com paginação e ações
- UserForm: Formulário reutilizável (create/edit)
- UserTypeSelector: Seletor com lógica condicional
- PDMSelector: Dropdown de PDMs disponíveis
- ConfirmDialog: Modal de confirmação para exclusão
Entendendo o Spring Security
O Spring Security é um framework poderoso e altamente personalizável que fornece autenticação e controle de acesso para aplicações Java, especialmente aquelas construídas com o Spring Framework. Ele oferece suporte a diversos métodos de autenticação, incluindo autenticação baseada em formulário, autenticação básica, OAuth2, e JWT (JSON Web Tokens), permitindo que os desenvolvedores implementem segurança de maneira flexível e eficaz. O Spring Security também permite a definição de regras de autorização por meio de anotações, como @PreAuthorize, que facilitam o controle de acesso a métodos e recursos com base nas permissões do usuário. Além disso, o framework integra-se de forma transparente com o modelo de segurança do Spring, permitindo o gerenciamento de sessões e a proteção contra vulnerabilidades comuns, como CSRF (Cross-Site Request Forgery) e ataques de força bruta, tornando-o uma escolha popular para proteger aplicações web e APIs RESTful.
Explicação:
- Cliente: O ponto de entrada que faz uma requisição HTTP.
- OncePerRequestFilter: Um filtro que é invocado para cada requisição, garantindo que a autenticação e a autorização sejam processadas.
- Authentication: O objeto que contém as informações do usuário autenticado.
- SecurityContextHolder: Um holder que armazena o contexto de segurança da requisição atual.
- Verificação de Autoridade: O processo de checar se o usuário autenticado possui a autoridade necessária.
- @PreAuthorize: Anotação que protege métodos com base nas autoridades do usuário.
- Requisição Processada: Se o usuário possui a autoridade, a requisição é processada normalmente.
- Acesso Negado: Se o usuário não possui a autoridade, um erro de acesso negado é retornado.
- Configuração de Segurança: Onde as configurações do Spring Security são definidas, incluindo o gerenciamento de sessões.
Observações:
- A configuração
sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))indica que a aplicação não manterá sessões e cada requisição é independente, o que é comum em APIs RESTful.
Protegendo Endpoints com o HasType
O componente HasType é uma anotação personalizada que combina a funcionalidade do Spring Security com a flexibilidade de anotações customizadas, permitindo que métodos ou classes sejam protegidos com base nas autoridades (permissions) do usuário. Ao ser aplicada a um método ou classe, essa anotação utiliza a expressão @PreAuthorize para verificar se o usuário autenticado possui a autoridade especificada no parâmetro value. Isso proporciona um controle de acesso mais granular, permitindo que os desenvolvedores definam facilmente quais roles ou permissões são necessárias para acessar determinadas funcionalidades da aplicação, simplificando a implementação de regras de segurança e tornando o código mais legível e organizado. Assim, HasType pode ser usado para proteger endpoints ou serviços, assegurando que somente usuários com as permissões adequadas possam executá-los.
Nota
Para gestão de acesso mais granular use a annotation @PreAuthorize que permite usar expressões mais avançadas
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/resource")
public class ResourceController {
@GetMapping("/secure-data")
@HasType("ADMIN") // Apenas usuários com a autoridade ROLE_ADMIN podem acessar este método
public ResponseEntity<String> getSecureData() {
String secureData = "Este é um dado seguro que só administradores podem acessar.";
return ResponseEntity.ok(secureData); // Retorna o dado seguro
}
}
Protegendo Rotas do NextJS com middlewares
A proteção baseada em middleware no Next.js é uma abordagem que permite implementar lógica de autenticação e autorização de forma centralizada, antes que as requisições cheguem às rotas específicas da aplicação. O middleware pode ser utilizado para interceptar requisições e verificar se um usuário está autenticado ou possui as permissões necessárias para acessar determinadas páginas ou recursos. Isso proporciona uma camada adicional de segurança, garantindo que apenas usuários autorizados possam acessar informações sensíveis ou ações restritas dentro da aplicação. Além disso, a utilização de middleware melhora a organização do código, uma vez que a lógica de segurança pode ser separada das funções de negócio.
Exemplo:
// middleware.js
import { NextResponse } from 'next/server';
import jwt from 'jsonwebtoken';
// Função para verificar se o usuário é ADMIN
export async function middleware(req) {
const token = req.cookies.token; // Supondo que o token JWT está armazenado em cookies
if (!token) {
// Se não houver token, redirecionar para a página de login
return NextResponse.redirect(new URL('/login', req.url));
}
const decoded = jwt.decode(token); // Decodifica o token sem verificar a assinatura
// Verifica se o tipo do usuário é ADMIN
if (!decoded || decoded.type !== 'ADMIN') {
return NextResponse.redirect(new URL('/unauthorized', req.url)); // Redireciona para página de acesso negado
}
// Se o tipo for ADMIN, permite o acesso à rota
return NextResponse.next();
}
// Configurando o middleware para as rotas /users
export const config = {
matcher: ['/users/:path*'],
};