📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados em Especialização em Backend com Java e Spring Boot. Recomendado revisar antes de começar.

🔑 Login de Usuários 02 — Refatoração SOLID/DRY

Trilha de Aprendizado — Projeto 2 de 4

🎓 Nível profissional simulado: Júnior. As features não mudam nada em relação ao _01 — o que muda é como o código é organizado. Um júnior já reconhece que “funciona” não é o mesmo que “está bem estruturado”, e sabe nomear o problema: violação de responsabilidade única, entidade JPA exposta direto no formulário web, ausência de validação de entrada.`

🎯 Objetivo

Este projeto parte do código do Login de Usuários 01 sem adicionar nenhuma feature nova — o objetivo é só refatorar. Comparar os dois projetos lado a lado é o próprio material de estudo.`

🔍 Os 3 Problemas Encontrados no Código do _01

Problema 1 — CustomUserDetailsService fazendo duas coisas

No _01, a classe que implementa UserDetailsService (contrato do Spring Security para autenticação) também tinha um método save(User user) usado pelo cadastro. Duas responsabilidades sem relação direta — autenticar vs. cadastrar — na mesma classe:

// _01 (antes)
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String email) { /* ... */ }

    public void save(User user) { /* lógica de cadastro aqui, misturada */ }
}

Fix: CustomUserDetailsService volta a fazer só uma coisa (loadUserByUsername). Toda a regra de negócio de usuário (cadastrar, listar, apagar, criar admin) migra para uma classe nova, UserService, usada tanto pelo AuthController (auto-cadastro) quanto pelo UserController (painel admin) — nenhum dos dois fala com o UserRepository diretamente.

Problema 2 — Entidade JPA exposta direto no formulário web

No _01, o formulário de cadastro fazia bind direto na entidade User:

// _01 (antes)
@PostMapping("/registar")
public String processRegistration(@ModelAttribute User user) {
    user.setRole(Role.USER);
    userService.save(user);
    return "redirect:/login?success";
}

Isso significa que o formulário HTML tem acesso indireto a todos os campos da entidade, incluindo role e id — só não vira uma falha de segurança real aqui porque o role é sobrescrito manualmente logo em seguida. Mas o desenho do código deixa essa porta aberta por padrão.

Fix: um DTO dedicado (RegisterUserDTO) só com os 3 campos que o formulário de fato deveria expor:

public class RegisterUserDTO {
    @NotBlank(message = "O nome é obrigatório")
    private String nome;

    @NotBlank(message = "O e-mail é obrigatório")
    @Email(message = "Informe um e-mail válido")
    private String email;

    @NotBlank(message = "A senha é obrigatória")
    @Size(min = 6, message = "A senha deve ter pelo menos 6 caracteres")
    private String senha;
    // getters/setters
}

role não existe no DTO — não porque alguém lembrou de filtrar, mas porque é estruturalmente impossível de enviar. Essa é a diferença entre “seguro por enquanto” e “seguro por construção”.

Problema 3 — Controller falando direto com o Repository

No _01, UserController injetava UserRepository e chamava findAll()/deleteById() direto — pulando a camada de serviço inteira:

// _01 (antes)
@Autowired
private UserRepository userRepository;

@GetMapping
public String listUsers(Model model) {
    model.addAttribute("users", userRepository.findAll());
    return "usuarios";
}

Fix: o controller passa a depender só de UserService. Hoje listAll()/deleteById() só repassam pro repositório, mas a regra de negócio tem onde crescer (ex.: “não permitir que um ADMIN se auto-apague”) sem tocar no controller.`

✅ Checkpoint

  1. UserRepository só é injetado dentro de classes de service/ — nenhum @Controller importa UserRepository (confira com grep -r UserRepository src/main/java/.../controller/, deve vir vazio).
  2. Tente cadastrar um usuário com e-mail inválido ou senha de 3 caracteres — a tela de cadastro deve mostrar a mensagem de erro em vez de deixar o H2 rejeitar silenciosamente.
  3. O comportamento visível da aplicação (quem consegue logar, quem é ADMIN, quais rotas são protegidas) é idêntico ao _01 — se algo mudou de comportamento, a refatoração foi longe demais.`

🏁 Executando o Projeto

cd loginusuarios
./mvnw spring-boot:run

Acesse http://localhost:8080/login — admin padrão: admin@email.com / admin123.


Voltar para Projetos

—`

🚀 Como Executar no Laboratório

1. Abra o terminal na pasta deste projeto

No seu editor/IDE, abra a pasta deste projeto (File > Open Folder) ou navegue via terminal:

cd javaweb_login_02_bcrypt

2. Execute a aplicação e os testes

./mvnw spring-boot:run
./mvnw test

[!TIP] Dica para execução a partir da raiz do repositório: Se você abriu o repositório completo no VS Code, basta navegar até a pasta antes de executar: cd proj_aplicacoes_full_stack/projetos/javaweb_login_02_bcrypt