🔑 API em Java e Spring Boot que visa validar uma senha informada pelo usuário. A senha deve conter alguns requisitos para que seja considerada válida:
- Possuir nove ou mais caracteres sem espaços em branco
- Possuir pelo menos um digíto
- Possuir pelo menos uma letra minúscula
- Possuir pelo menos uma letra maiúscula
- Possuir pelo menos um caractere especial, sendo considerados como especiais os caracteres: !@#$%^&*()-+
- Não possuir caracteres repetidos
Existem alguns pré-requisitos antes da execução desta API. Você deverá possuir os itens abaixo:
- OpenJDK17
- Git
- A IDE de sua preferência - Eclipse / Intellij IDEA / Spring Tool Suite
#Clone o repositório
git clone https://github.com/jvfranco/passwordValidation.git
#Inicie a aplicação na IDE escolhida
#Para acesso via Swagger, utilize o endereço em seu navegador de preferência
http://localhost:8080/swagger-ui/index.html#/
#Caso queira realizar os testes através do Postman, criar uma requisição POST para o endereço:
http://localhost:8080/password/validation
#E enviar o json com a senha no body da requisição:
{
"password": "AbTp9!fok"
}
- Java 11
- Spring Boot 3.0.1
- Banco H2
- OpenAPI
- Lombok
- Testes Unitários com JUnit 5 e Mockito
- Criptografia de senha com BCryptPasswordEncoder
- Regex
- Design Patterns: Builder e Chain of Responsibility
-
Requisição: POST /password/validation
-
Body da requisição:
Parâmetro | Tipo | Descrição |
---|---|---|
password | String | Senha que será validada |
{
"password": "AbTp9!fok"
}
- Retorno
Status | Parâmetro | Tipo | Descrição |
---|---|---|---|
200 OK | isValid | Boolean | true caso a senha seja valida e false caso inválida |
{
"isValid": true
}
Para criação da solução pensei em dois princípios da Orientação a Objetos, a Coesão e o Acoplamento. Criei uma classe para cada uma das validações, buscando evitar grandes classes com diversos métodos, melhorando a leitura e entendimento do código. Para executar as validações pensei em utilizar expressões regulares, não utilizo comumente em meu dia a dia, o que necessitou de pesquisas adicionais para implementação no desafio. Para validação de caracteres duplicados utilizei a coleção set, que não permite itens duplicados, verificando o tamanho da senha antes e pós adição na collection. Para organização dos pacotes, pensei em separar as classes relacionadas a entidade, Password, das classes relacionados a regras de negócio das validações. Também utilizei o design pattern Chain of Responsibility.
-
Para Execução das validações utilizei expressões regulares, não estou habituado em utilizar a classe Pattern do Java, o que demandou pesquisa e leitura da documentação.
-
Na validação de caracteres duplicados utilizei uma collection Set, no caso HashSet, faço um split na senha e depois adiciono os caracteres na collection, como os sets não permitem itens duplicados, caso haja caracteres duplicados, um não será inserido na collection, o que modificará o tamanho da senha, com isso comparo o tamanho da collection com a senha original e verifico se estão ou não com o mesmo tamanho.
-
Para a separação dos pacotes, pensei em deixar as classes relacionadas ao modelo da senha em um pacote denominado domain, onde tenho acesso a base de dados. Outro pacote denominado configuration, onde deixei as classes de configuração, no caso somente a configuração do SpringDoc OpenAPI. E um terceiro pacote denominado application, onde coloquei as classes de modelo de request e response, utilizei as classes Record do Java, para manter a imutabilidade dos objetos, controller com um endpoint POST ( /password/validation ) e as classes de validação da senha, agrupando as regras de negócio.
-
Resolvi deixar as classes de validação com somente um método em cada, tentando criar classes coesas, com poucos atributos e somente uma função, penso que classes sem coesão tendem a crescer demasiadamente conforme evolução da aplicação, o que acaba descaracterizando o motivo de sua criação. Busquei atender o primeiro princípio do SOLID, o Princípio da Responsabilidade Única.
-
No desenvolvimento percebi que as validações poderiam ser realizadas com vários ifs, então resolvi pesquisar por um design pattern que se encaixasse no meu desenvolvimento, e encontrei o Chain of Responsibility, no qual crio uma corrente ( chain ) com todas as classes das validações que desejo realizar e conforme as validações se confirmam elas próprias chamam a próxima validação, caso a senha não seja validada lanço uma exception ( ValidatorException ), a qual encerra a execução das validações e informo através de logs o motivo da não validação, nesse caso o retorno da API será false e a senha não será gravada em banco. Quando a senha é validada, é gravada em banco depois de ser criptografada com a classe BCryptPasswordEncoder e o retorno da API é true.
-
Deixei o retorno da API com código HTTP 200 OK, mesmo que a senha não tenha sido validada, deixei desta forma porque demonstra que ocorreu o processamento e que a senha não passou nos critérios pré definidos.