Baixe o app para aproveitar ainda mais
Prévia do material em texto
Spring Security No mundo da tecnologia, em especial da Internet, nenhuma aplicação em execução na Nuvem pode ficar sem algum �po de Segurança habilitada, devido aos inúmeros perigos existentes no mundo virtual como ataques Hackers, invasões de Servidores, roubos de dados, entre outros. No ecossistema Spring, isso é feito com a ajuda da Dependência Spring Security. Vamos adicionar a Dependência Spring Security no Projeto Blog Pessoal, adicionando as linhas abaixo no arquivo pom.xml, Salvar e Executar a aplicação: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> Abra a sua aplicação no Navegador da Internet através do endereço: h�p://localhost:8080 e veja o que acontece. http://localhost:8080/ Como num passe de mágica… … Você tem uma página de login gerada automa�camente. … Você não pode mais executar solicitações GET, POST, PUT e DELETE. … Todo o seu aplica�vo está bloqueado e solicita que você insira um nome de usuário e uma senha, que não existe. Tendo sobrevivido ao susto inicial, você deve ter pensado: Como tudo isso aconteceu? 1. Segurança da Aplicação Antes de estudarmos a Spring Security, vamos compreender 3 conceitos importantes da Segurança da Informação: 1.1. Auten�cação É o primeiro processo da Segurança da Informação, popularmente conhecido como Login no sistema. É o momento em que o usuário informa o seu usuário de acesso (e- mail) e a sua senha (criptografada), e o sistema fará a checagem se estas informações estão corretas. 1.2. Autorização É o segundo processo da Segurança da Informação, popularmente conhecido como Direitos de acesso (Roles) no sistema. É o momento em que o sistema checará o que o usuário pode e não pode fazer no sistema, ou seja, as suas permissões dentro do sistema (Quais Recursos e Endpoints podem ser acessados?). 1.3. Filtros de Servlet Qualquer aplica�vo da web Spring é apenas um Servlet (é uma Classe Java usada para criar aplicações WEB), que redireciona todas as Requisições HTTP recebidas (por exemplo, do Front-end Angular ou React), para as suas respec�vas Classes Controladoras (@RestControllers). Como nestas requisições não existe uma segurança e a auten�cação e a autorização devem ser efetuadas antes das Requisições HTTP, a Spring Security oferece como solução para este problema os Filtros, que na prá�ca são Objetos que interceptam toda e qualquer Requisição HTTP recebida antes de chegarem ao controlador. Por isso que o seu Blog Pessoal está “trancado” após a inserção da Dependência Spring Security. A figura acima, ilustra o filtro BasicAuthen�ca�onFilter checando o Token de Autorização do Usuário enviado no cabeçalho da requisição. Como ele está dentro do padrão Basic, o filtro libera o envio da requisição para a Classe Controladora específica. A Dependência Spring Security ao ser inserida, habilita 15 filtros, onde cada um tem uma função específica e precisam ser configurados. Não abordaremos este assunto em detalhes por ser muito extenso, além de demandar tempo e dedicação, de qualquer forma você já sabe como a sua aplicação ficou totalmente bloqueada. A Imagem abaixo mostra os 15 filtros: 2. Spring Security Analisando nossos projetos podemos perceber que nossa API, até este momento, não possuía nenhuma segurança, ou seja, qualquer pessoa poderia acessar todos os nossos endpoints e ter acesso à todos os recursos livremente. Precisamos entender que algumas aplicações contém informações vitais como: dados pessoais, dados bancários, usuário e senha de acesso, e portanto precisamos garan�r que a nossa API e estes dados estejam devidamente protegidos. E para isto podemos contar com a dependência do Spring chamada Spring Security (adicionada acima). A Spring Security é um framework para Java, que provê auten�cação, autorização, filtros e diversas outras funcionalidades para aplicações corpora�vas, com o obje�vo de proteger a nossa aplicação contra acessos indevidos. Iniciado em 2003 por Ben Alex, a Spring Security é distribuída sob a licença Apache Licence. Ela oferece diversos recursos que permitem que muitas prá�cas comuns de segurança sejam implementadas ou configuradas de forma direta. O esquema de auten�cação que u�lizaremos em nosso projeto é o HTTP Basic, onde entraremos com o e-mail (usuário) e a senha do usuário e através de um endpoint liberado, o Spring Security irá criptografar a senha e fazer uma consulta no nosso Banco de dados para saber se o usuário já existe. Esta checagem será feita através da Camada de Serviço (service) da aplicação. Se a consulta encontrar o usuário e a senha, a Spring Security devolverá como resposta um Authoriza�on com o prefixo Basic + token. Este token ficará registrado na nossa aplicação na camada de Security, e apenas por meio dele que o usuário poderá consumir a API. Para melhor compreensão deste sistema, é necessário dividi-lo em 2 ecossistemas: Usuário e Segurança, que veremos mais a frente. 3. Conhecendo HTTP authen�ca�on O IETF (Internet Engineering Task Force) tem como missão iden�ficar e propor soluções para as questões/problemas relacionados à u�lização da Internet, além de propor a padronização das tecnologias e protocolos envolvidos. O mesmo define a estrutura de auten�cação HTTP que pode ser usada por um servidor para definir uma solicitação do cliente. O servidor responde ao cliente com uma mensagem do �po HTTP Status 401(Não autorizado) e fornece informações de como autorizar com um cabeçalho de resposta WWW-Authen�cate contendo ao menos uma solicitação. Um cliente que deseja auten�car-se com um servidor pode fazer isso incluindo um campo de cabeçalho de solicitação WWW-Authen�cate com as credenciais. No Diagrama de Sequência abaixo pode se observar este relacionamento: No caso de uma autorização “Basic” (como a mostra a figura acima), a troca deve acontecer por meio de uma conexão HTTP (TLS) para ser segura. Se um servidor recebe credenciais válidas, mas que não são adequadas para ter acesso a um determinado recurso, o servidor responderá com o código de status HTTP Status 403 (Proibido!) . Ao contrário de HTTP Status 401(Não autorizado), a auten�cação é impossível para este usuário. O cabeçalho de requisição Authoriza�on contém as credenciais para auten�car um agente de usuário com um servidor. Aqui o �po é novamente necessário, seguido pelas credenciais, que podem ser codificadas ou criptografadas dependendo do esquema de auten�cação usado. No caso acima foi u�lizado o esquema de auten�cação Basic que será explicado na sequencia. Documentação: HTTP Status Code 401 - Unauthorized Documentação: HTTP Status Code 403 - Forbidden Documentação: Cabeçalho HTTP WWW-Authen�cate Documentação: Cabeçalho de Requisição HTTP Authoriza�on https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Status/401 https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Status/403 https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Headers/WWW-Authenticate https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Headers/Authorization 3.1. Esquema de auten�cação Basic A estrutura geral de auten�cação HTTP é usado por vários esquemas de auten�cação. Os esquemas podem divergir na força da segurança e na disponibilidade do so�ware cliente ou servidor. O esquema mais comum de auten�cação é o “Basic”, mas existem outros esquemas oferecidos por serviços de hospedagem, como Amazon AWS, Google ou Microso�. Os esquemas de auten�cação mais comuns são: Esquema de auten�cação Documentação Basic [RFC7617] Bearer [RFC6750] Digest [RFC7616] HOBA [RFC7486, Sec�on 3] Mutual [RFC8120] Nego�ate [RFC4559, Sec�on 3] OAuth [RFC5849, Sec�on 3.5.1] SCRAM-SHA-1 [RFC7804] SCRAM-SHA-256 [RFC7804] vapid [RFC 8292, Sec�on 3] ATENÇÃO: Para melhor compreensão no momento, vamos focar apenas no entendimento do formato Basic, que é considerado o principal esquema para compreender os demais meios de autorização. Vale mencionar que para aprender os demais é necessário tempo e muita dedicação. O esquemaBasic, segundo sua documentação, consiste em um conjunto de caracteres que posicionados após a palavra "Basic " no formato “email:senha” codificados u�lizando o modelo Base64, formando um token Authoriza�on para ser passado ao sistema. Abaixo veremos um exemplo de código para gerar a estrutura em Java: https://www.iana.org/go/rfc7617 https://www.iana.org/go/rfc6750 https://www.iana.org/go/rfc7616 https://www.iana.org/go/rfc7486 https://www.iana.org/go/rfc8120 https://www.iana.org/go/rfc4559 https://www.iana.org/go/rfc5849 https://www.iana.org/go/rfc7804 https://www.iana.org/go/rfc7804 https://www.iana.org/go/rfc8292 Dependência: <!-- Dependência para Codificação do Token --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> </dependency> Exemplo de código em java: O resultado do código acima proporciona o retorno de um esquema de auten�cação Basic respeitando as regras ja definidas pela IETF (Internet Engineering Task Force). Exemplo: Usuário: admin@email.com.br Senha: admin123 Token gerado: Basic YWRtaW5AZW1haWwuY29tLmJyOmFkbWluMTIz ALERTA DE BSM: Mantenha a Atenção aos Detalhes ao escrever o formato Basic, o mesmo é representado da palavra "Basic " com um espaço na frente + a criptografia de um conjunto de caracteres (email:senha) fornecidos ao se auten�car no sistema. No link abaixo é possível testar a codificação 64 bits. Site: Codificador 64 bits O Token gerado será enviado no Header (cabeçalho) de uma requisição ao servidor devidamente configurado para acessar os seus recursos. import java.nio.charset.Charset; import org.apache.commons.codec.binary.Base64; private static String geradorBasicToken(String email, String senha) { String estrutura = email + ":" + senha; byte[] estruturaBase64 = Base64.encodeBase64(estrutura.getBytes(Charset.forName("US-ASCII"))) return "Basic " + new String(estruturaBase64); } mailto:admin@email.com.br https://www.base64url.com/ Projeto 02 - Blog Pessoal - Spring Security 01 O que veremos por aqui: 1. O Ecossistema do Usuário 2. Apresentação do Recurso Usuário 3. Criar a Classe Model Usuario relacionada com a Classe Postagem 4. Criar a Classe UsuarioLogin 5. Criar a Interface UsuarioRepository 1. Ecossistema do Usuario No Diagrama de Classes acima, é possível visualizar por completo o ecossistema do Usuário, que representa uma das principais implementações, que permitem que o sistema de auten�cação baseado nos detalhes do usuário (UserDetail) funcione. A cor de cada uma das classe representa o pacote onde cada Classe deve ser construída. segundo as informações da Legenda. Para melhor compreensão das relações entre as Classes, veja abaixo uma breve definição sobre as relações entre os Objetos: Relação Descrição Dependência Classe A pode ser afetada por mudanças na classe B; Associação Objeto A sabe sobre objeto B. Classe A depende de B; Agregação Objeto A sabe sobre objeto B, e consiste de B. Classe A depende de B; Composição Objeto A sabe sobre objeto B, consiste de B, e gerencia o ciclo de vida de B. Classe A depende de B; Implementação Classe A define métodos declarados na interface B. objetos de A podem ser tratados como B. Classe A depende de B; Herança Classe A herda a interface e implementação da classe B mas pode estende-la. Objetos de A podem ser tratados como B. Classe A depende de B. Após compreender o Diagrama de Classes acima e aprender um pouco sobre UML, segue a aplicação do código em cada uma de suas classes. ATENÇÃO: Aproveite para validar seu código do Blog Pessoal, o projeto abaixo serve como espelho para o projeto. Tenha bastante atenção aos detalhes, pois existem implementações fundamentais para o perfeito funcionamento do sistema. Caso surjam dúvidas, não hesite em buscar os Instrutores Genera�on para obter orientações. 2. O Recurso Usuario Nesta etapa vamos começar a construir o Recurso Usuario, que será composto por 2 Classes: Usuario e Usuario Login. A Classe Usuario servirá de modelo para construir a tabela tb_usuario (En�dade) dentro do nosso Banco de dados db_blogpessoal. Os campos (Atributos) da tabela serão os mesmos que estão definidos no Diagrama de Classes acima. Além de construirmos a Classe Usuario, também faremos o Relacionamento com as Classe Postagem, construída anteriormente. Veja como ficará o Relacionamento entre as 3 Classes, após a implementação, no Diagrama de Classes abaixo: Na sequência vamos construir a Interface UsuarioRepository, que irá nos auxiliar na interação com o Banco de dados e com as Classes UsuarioController, onde serão implementados os 5 métodos descritos no Diagrama de Classes acima e com a Classe de Serviço (Service), UsuarioService, onde serão implementadas as Regras de Negócio exigidas pela Spring Security. O Diagrama de Classes da nossa Interface UsuarioRepository será igual a imagem abaixo: Depois de criar a Classe Model Usuario e Relacionamento com a Classe Postagem, a estrutura do nosso Banco de dados db_blogpessoal ficará igual ao Diagrama de En�dade e Relacionamentos (DER) abaixo: Observe que a en�dade Postagem (tb_postagem) passará a ter 2 Chaves Estrangeiras (FK): tema_id e usuario_id. O Dicionário de dados da nossa tabela tb_Usuario será o seguinte: Atributo Tipo de dado Descrição Chave id bigint Iden�ficador único PK nome varchar(255) Nome do usuário usuario varchar(255) E-mail do usuário senha varchar(255) Senha do usuário foto varchar(5000) Foto do usuário ALERTA DE BSM: Mantenha a Atenção aos Detalhes ao criar o Recurso Usuario. Todas as Camadas (Pacotes: Model, Repository e Controller), já foram criadas nos Recursos Postagem e Tema. ALERTA DE BSM: Mantenha a Atenção aos Detalhes ao criar o Recurso Usuario. Todas as Camadas (Pacotes: Model, Repository e Controller), já foram criadas nos Recursos Postagem e Tema. DICA: Caso você tenha alguma dúvida sobre como criar a Classe, executar o projeto, entre outras, consulte a Documentação dos Recursos Postagem e Tema. Além da Classe Usuario e da Interface UsuarioRepository, criaremos a Classe Usuario Login, que embora seja criada na Camada Model, ela não irá gerar uma tabela no Banco de dados. Esta Classe será uma Classe auxiliar para o usuário efetuar login no sistema. 👣 Passo 01 - Checar as Dependências No arquivo, pom.xml, adicione as linhas abaixo (caso ainda não tenha adicionado alguma delas): <!-- Dependência da Spring Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Dependência para Codificação do Token --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> </dependency> Código fonte: pom.xml 👣 Passo 02 - Criar a Classe Usuario na Camada Model Agora vamos criar a terceira Classe Model que chamaremos de Usuario. 1. Clique com o botão direito do mouse sobre o Pacote Model (com.genera�on.blogpessoal.model), na Source Folder Principal (src/main/java), e clique na opção New 🡪 Class 2. Na janela New Java Class, no item Name, digite o nome da Classe (Usuario), e na sequência clique no botão Finish para concluir. A seguir, veja a sua implementação: package com.generation.blogpessoal.model; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @Entity @Table(name = "tb_usuarios") https://github.com/conteudoGeneration/blog-pessoal/blob/12-Blog-(Security_PomXML_Model_Usuario_And_UserLogin)/blogpessoal/pom.xml public class Usuario{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotNull(message = "O atributo Nome é Obrigatório!") private String nome; @Size(max = 5000, message = "O link da foto não pode ser maior do que 5000 caracteres") private String foto; @NotNull(message = "O atributo Usuário é Obrigatório!") @Email(message = "O atributo Usuário deve ser um email válido!") private String usuario; @NotBlank(message = "O atributo Senha é Obrigatório!") @Size(min = 8, message = "A Senha deve ter no mínimo 8 caracteres") private String senha; @OneToMany(mappedBy = "usuario", cascade = CascadeType.REMOVE) @JsonIgnoreProperties("usuario") private List<Postagem> postagem; /* Insira os Getters and Setters */ public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getFoto() { return foto; } public void setFoto(String foto) { this.foto = foto; } public String getUsuario() { return usuario; } public void setUsuario(String usuario) { this.usuario = usuario; } public String getSenha() { return senha; } public void setSenha(String senha) { this.senha = senha; } public List<Postagem> getPostagem() { return postagem; } public void setPostagem(List<Postagem> postagem) { this.postagem = postagem; } } ALERTA DE BSM: Mantenha a Atenção aos Detalhes, o atributo senha definido acima, não pode conter o atributo size max, que limita seu tamanho máximo. Dependendo do limite inserido, pode provocar um erro 500, devido ao limite de caracteres que serão u�lizados na criptografia. Deixe apenas com size min configurado. ALERTA DE BSM: Mantenha a Atenção aos Detalhes, o atributo de relacionamento definido como postagem, possui a anotação @OneToMany com a opção cascade type = REMOVE. Se o seu projeto es�ver com ALL, ao deletar uma postagem você apagará também o usuário, pois este relacionamento tem uma dependência de pai e filho. Mantenha como REMOVE para que isso não aconteça Para concluir, não esqueça de Salvar o código (File 🡪 Save All). 👣 Passo 03 - Criar a Relação ManytoOne na Classe Postagem A Classe Postagem será o lado N:1, ou seja, Muitas Postagens podem ter apenas Um Usuario. Para criar a Relação vamos inserir depois do úl�mo atributo da Classe Postagem (tema), as 3 linhas destacadas em vermelho na figura abaixo: Não esqueça de acrescentar acrescentar os Métodos Get e Set para o novo atributo (usuario), que foi adicionado na Classe Postagem. DICA: Toda vez que você adicionar um novo Atributo na sua Classe, não esqueça de criar os Métodos GET e SET do Atributo. Caso contrário, você não conseguirá visualizar ou atualizar os dados do Atributo. 👣 Passo 04 - Criar a Classe UsuarioLogin na Camada Model Agora vamos criar a quarta Classe Model que chamaremos de UsuarioLogin. A classe UsuarioLogin é responsável por definir que o cliente ao tentar auten�car (fazer login) no sistema, forneça apenas o usuário (e-mail) e a senha. Essa classe também pode ser definida como uma DTO (Data trasfer object), que é uma classe que é u�lizada para transitar dados do sistema sem revelar sua Classe Model para o cliente. 1. Clique com o botão direito do mouse sobre o Pacote Model (com.genera�on.blogpessoal.model), na Source Folder Principal (src/main/java), e clique na opção New 🡪 Class 2. Na janela New Java Class, no item Name, digite o nome da Classe (UsuarioLogin), e na sequência clique no botão Finish para concluir. A seguir veja sua implementação: package com.generation.blogpessoal.model; public class UsuarioLogin { private Long id; private String nome; private String usuario; private String foto; private String senha; private String token; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getUsuario() { return usuario; } public String getFoto() { return foto; } public void setFoto(String foto) { this.foto = foto; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } public void setUsuario(String usuario) { this.usuario = usuario; } public String getSenha() { return senha; } public void setSenha(String senha) { this.senha = senha; } } ALERTA DE BSM: Mantenha a Atenção aos Detalhes, não se esqueça de passar o id do usuario como atributo da classe, pois este atributo será u�lizado como crdencial pelo front-end para pegar o usuario pelo id. Todos os atributos presentes na Classe Model Usuario devem estar presentes na Classe UsuarioLogin. ALERTA DE BSM: Mantenha a Atenção aos Detalhes, não se esqueça de passar o id do usuario como atributo da classe, pois este atributo será u�lizado como crdencial pelo front-end para pegar o usuario pelo id. Todos os atributos presentes na Classe Model Usuario devem estar presentes na Classe UsuarioLogin. ALERTA DE BSM: Mantenha a Atenção aos Detalhes, não esquecer também do atributo token, pois o mesmo será passado no cabeçalho de todas as requisições que do front-end. Este atributo é fundamental para o funcionamento do consumo da api. Para concluir, não esqueça de Salvar o código (File 🡪 Save All). 👣 Passo 05 - Executar o projeto e Checar o Banco de dados 1. Execute o projeto e verifique no Console se a tabela tb_usuario foi criada. 2. Verifique também no MySQL Workbench se a tabela tb_usuario foi criada no Banco de dados db_blogpessoal com o respec�vo relacionamento. 👣 Passo 06 - Criar a Interface UsuarioRepository na Camada Repository Agora vamos criar a Interface Repository que chamaremos de UsuarioRepository. 1. Clique com o botão direito do mouse sobre o Pacote Repository (com.genera�on.blogpessoal.repository), na Source Folder Principal (src/main/java): 2. Na sequência, clique na opção New 🡪 Interface 3. Na janela New Java Interface, no item Name, digite o nome da Interface (PostagemRespository), e na sequência clique no botão Finish para concluir. A seguir veja sua implementação: package com.generation.blogpessoal.repository; import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.generation.blogpessoal.model.Usuario; @Repository public interface UsuarioRepository extends JpaRepository<Usuario, Long>{ public Optional<Usuario> findByUsuario(String usuario); } ALERTA DE BSM: Mantenha a Atenção aos Detalhes, tome muito cuidado pois se a escrita dos métodos findBy ou findAllBy es�ver errada, pode aparecer um erro no console do STS. Observe que foi criada uma Query Method na Interface UsuarioRepository, conforme detalhado abaixo: Query Method public Optional<Usuario> findByUsuario(String usuario); Instrução SQL equivalente SELECT * FROM tb_usuario WHERE usuario = "usuario"; Palavra Instrução SQL find 🡪 SELECT By 🡪 WHERE Usuario 🡪 Atributo da Classe Usuario Palavra Instrução SQL String usuario 🡪 Parâmetro do Método contendo o e-mail do usuário que você deseja procurar. ATENÇÃO: A instrução FROM tb_usuario será inserida pelo JPA ao checar o nome da tabela gerada pela Classe Usuario. Para concluir, não esqueça de Salvar o código (File 🡪 Save All). Código fonte: Camada Model e Repository https://github.com/conteudoGeneration/blog-pessoal/tree/15-Blog-(Security_UserDetailsService_And_UserRepository) Projeto 02 - Blog Pessoal - Spring Security 02 O que veremos por aqui: 1. O Ecossistema da Segurança 2. A Classe UserDetailsImpl 3. A Classe UserDetailsServiceImpl 4. A Classe BasicSecurityConfigAntes de finalizarmos o Ecossistema do Usuário, vamos compreender e implementar o Ecossistema da Segurança, porquê u�lizaremos alguns Métodos deste ecossistema na implementação das Classes UsuarioService e UsuarioController. 1. Ecossistema da Segurança No Diagrama de Classes acima, é possível visualizar por completo o ecossistema da Segurança, que representa uma das principais implementações de segurança do sistema. Nesta implementação é possível realizar a auten�cação do usuário a par�r de um Banco de dados. Para isso é necessário enviar as informações necessárias para que a Spring Security valide a permissão do usuário no sistema. O ecossistema de segurança consiste de uma classe de configuração (BasicSecurityConfig), uma de Classe Serviço (UserDetailsService) e uma Classe Model (UserDetailsImpl). A cor de cada uma das Classes representa o pacote onde cada uma deve ser implementada, segundo informações da legenda. Para este Diagrama de Classes, repare que a Classe Model do Usuario e o Repositório do Usuário já foram criadas anteriormente. Para melhor compreensão das relações entre as Classes veja uma breve definição sobre as relações entre os Objetos: Relação Descrição Dependência Classe A pode ser afetada por mudanças na classe B; Associação Objeto A sabe sobre objeto B. Classe A depende de B; Agregação Objeto A sabe sobre objeto B, e consiste de B. Classe A depende de B; Composição Objeto A sabe sobre objeto B, consiste de B, e gerencia o ciclo de vida de B. Classe A depende de B; Implementação Classe A define métodos declarados na interface B. objetos de A podem ser tratados como B. Classe A depende de B; Herança Classe A herda a interface e implementação da classe B mas pode estende-la. Objetos de A podem ser tratados como B. Classe A depende de B. Após compreender o Diagrama de Classes acima e aprender um pouco sobre UML, segue a aplicação do código em cada uma de suas Classes. 👣 Passo 01 - Criar o Pacote Security Primeiro vamos criar a camada de segurança, ou seja, o Pacote Security, onde as Classes serão implementadas: 1. No lado esquerdo superior, na Guia Package explorer, clique com o botão direito do mouse sobre a Package com.genera�on.blogpessoal, na Source Folder src/main/java e clique na opção New 🡪 Package. 2. Na janela New Java Package, no item Name, acrescente no final do nome da Package .security, como mostra a figura abaixo: 3. Clique no botão Finish para concluir. 👣 Passo 02 - Criar a Classe UserDetailsImpl na Camada Security Esta classe implementa a Interface UserDetails, que tem como principal funcionalidade fornecer as informações básicas do usuário para a Spring Security. Ao criar esta Classe, será permi�do que uma Classe de Serviço (Service), preencha os atributos através do Método Construtor e retorne apenas o necessário para que a Spring Security possa validar o usuário que está tentando acessar o sistema. 1. Clique com o botão direito do mouse sobre o Pacote Security (com.genera�on.blogpessoal.security), na Source Folder Principal (src/main/java), e clique na opção New 🡪 Class 2. Na janela New Java Class, no item Name, digite o nome da Classe (UserDetailsImpl), e na sequência clique no botão Finish para concluir. ATENÇÃO: Por se tratar de uma Implementação de uma Interface (UserDetails), todos os Métodos da Interface devem ser implementados e o nome da Classe deve obrigatoriamente conter o sufixo Impl (Implements), indicando que a Classe está Implementando a Interface. Vejamos abaixo a implementação da Classe: package com.generation.blogpessoal.security; import java.util.Collection; import java.util.List; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import com.generation.blogpessoal.model.Usuario; public class UserDetailsImpl implements UserDetails{ private static final long serialVersionUID =1L; private String userName; private String password; private List<GrantedAuthority> authorities; public UserDetailsImpl (Usuario user){ this.userName = user.getUsuario(); this.password = user.getSenha(); } public UserDetailsImpl (){} @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return password; } @Override public String getUsername() { return userName; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } Para concluir, não esqueça de Salvar o código (File 🡪 Save All). ALERTA DE BSM: Mantenha a Atenção aos Detalhes, é necessário ter um construtor adaptado para receber atributos que serão u�lizados para logar no sistema. Os atributos em questão são usuário e senha, mas poderia ser também e-mail e senha. Tudo irá depender de como a model de Usuario esta elaborada. ATENÇÃO: *Observe que o Método getAuthori�es(), que retorna a lista com os direitos de acesso do usuário, sempre retornará uma List vazia, porquê este atributo não pode ser Nulo. Com o obje�vo de simplificar a implementação, todo o Usuário auten�cado terá todos os direitos de acesso sobre a aplicação. * Documentação: UserDetails Documentação: GrantedAuthority 👣 Passo 03 - Criar a Implementação da Classe UserDetailsServiceImpl Esta Classe é uma implementação da Interface UserDetailsService, responsável por fornecer a estrutura de um método u�lizado pela Spring Security para validar a existência de um usuário no Banco de dados e retornar um UserDetails. Com os dados fornecidos ao sistema, será possível auten�car um usuario baseando-se na sua existência no banco. Vale lembrar que para isso é necessário que ao salvar o usuário, sua senha esteja criptografada (Veremos na implementação da Classe UsuarioService), u�lizando uma biblioteca de criptografia válida para o sistema de segurança. https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/core/userdetails/UserDetails.html https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/core/GrantedAuthority.html 1. Clique com o botão direito do mouse sobre o Pacote Security (com.genera�on.blogpessoal.security), na Source Folder Principal (src/main/java), e clique na opção New 🡪 Class 2. Na janela New Java Class, no item Name, digite o nome da Classe (UserDetailsServiceImpl), e na sequência clique no botão Finish para concluir. ATENÇÃO: Por se tratar de uma Implementação de uma Interface (UserDetailsService), todos os Métodos da Interface devem ser implementados e o nome da Classe deve obrigatoriamente conter o sufixo Impl (Implements), indicando que a Classe está Implementando a Interface. Vejamos abaixo a implementação da Classe: package com.generation.blogpessoal.security; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import com.generation.blogpessoal.model.Usuario; import com.generation.blogpessoal.repository.UsuarioRepository; @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UsuarioRepository repository; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { Optional<Usuario> usuario = repository.findByUsuario(userName); usuario.orElseThrow(() -> new UsernameNotFoundException(userName + " return usuario.map(UserDetailsImpl::new).get();} } Observe que na linha return usuario.map(UserDetailsImpl::new).get();, a Classe Retorna um objeto do �po UserDetailsImpl criado com os dados recuperados do Banco de dados. O operador :: faz parte de uma expressão que referencia um Método, complementando uma função Lambda. Neste exemplo, o operador faz referência ao Método Construtor da Classe UserDetailsImpl. Esta instrução poderia é equivalente ao seguinte comando: return new UserDetailsImpl(op�onal.get());. ALERTA DE BSM: Mantenha a Atenção aos Detalhes, ao criar este serviço não esquecer de colocar a anotação @Service. Desta forma o Spring entenderá que a classe é um serviço e fará o carregamento com prioridade para esta classe. Documentação: UserDetailsService 👣 Passo 04 - Criar a Classe BasicSecurityConfig Esta Classe é u�lizada para sobrescrever a configuração já predefinida pela Spring Security para a segurança. A Classe possui a anotação @EnableWebSecurity, que habilita e sobrescreve os Métodos que irão redefinir as regras de Segurança de acordo com as Regras da sua aplicação. 1. Clique com o botão direito do mouse sobre o Pacote Security (com.genera�on.blogpessoal.security), na Source Folder Principal (src/main/java), e clique na opção New 🡪 Class 2. Na janela New Java Class, no item Name, digite o nome da Classe (BasicSecurityConfig), e na sequência clique no botão Finish para concluir. package com.generation.blogpessoal.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication .builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web .builders.HttpSecurity; import org.springframework.security.config.annotation.web .configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web .configuration.WebSecurityConfigurerAdapter; https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/core/userdetails/UserDetailsService.html import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @EnableWebSecurity public class BasicSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); auth.inMemoryAuthentication() .withUser("root") .password(passwordEncoder().encode("root")) .authorities("ROLE_USER"); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/usuarios/logar").permitAll() .antMatchers("/usuarios/cadastrar").permitAll() .antMatchers(HttpMethod.OPTIONS).permitAll() .anyRequest().authenticated() .and().httpBasic() .and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().cors() .and().csrf().disable(); } } Na implementação acima a anotação @Bean no método passwordEncoder() , indica ao Spring Security que a aplicação está baseada em algum modelo de criptografia, que será implementada no Método PasswordEncoder. Para esta aplicação estamos u�lizando o modelo BCryptPasswordEncoder(). Vale ressaltar que esta criptografia está sendo esperada para analisar a senha que estamos guardando no Banco de dados. Dica: No primeiro link abaixo você pode conhecer mais sobre o BCrypt e no segundo link testar a codificação e a decodificação de Strings através do BCrypt. Ar�go: Uma breve introdução sobre BCrypt Site: Codificador BCrypt Quando sobrescrevemos o método ***configure(Authen�ca�onManagerBuilder auth)***, estamos informando ao spring que a configuração de auten�cação será redefinida em sua implementação. Neste método estamos fornecendo duas formas de auten�cação: .userDetailsService(userDetailsService): u�liza um serviço para pesquisar a credencial do usuário no Banco de dados. .inMemoryAuthen�ca�on(): u�liza um usuário em memória, no caso definido no próprio método, como usuário root e senha root. Para mais detalhes sobre esta definição, o mais correto é olhar sua fonte no link abaixo: Documentação: Authen�ca�onManagerBuilder Quando sobrescrevemos o método ***configure(H�pSecurity h�p)***, estamos informando ao Spring que a configuração por padrão definida será alterada. Nesta configuração é possivel customizar de diferentes maneiras a sua aplicação. .authorizeRequests(): podemos definir quais endpoints poderão acessar o sistema sem precisar de auten�cação. No caso acima foi definido que os endpoints logar e cadastrar serão autorizados para todos os usuários, enquanto para os demais endpoints será necessário auten�car. .antMatchers("/usuarios/logar").permitAll() e .antMatchers("/usuarios/cadastrar").permitAll(): permite definir o caminho do endpoint que estará acessível sem auten�cação. https://medium.com/reprogramabr/uma-breve-introdu%C3%A7%C3%A3o-sobre-bcrypt-f2fad91a7420 https://bcrypt-generator.com/ https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/config/annotation/authentication/builders/AuthenticationManagerBuilder.html ATENÇÃO: Os endpoints cadastrar e logar foram liberados porquê caso contrário ninguém conseguirá acessar a aplicação. Além disso, é fundamental que os endereços dos endpoints: /usuarios/logar e /usuarios/cadastrar estejam definidos na implementação da Classe UsuarioController. ATENÇÃO: Os endpoints cadastrar e logar foram liberados porquê caso contrário ninguém conseguirá acessar a aplicação. Além disso, é fundamental que os endereços dos endpoints: /usuarios/logar e /usuarios/cadastrar estejam definidos na implementação da Classe UsuarioController. .antMatchers(H�pMethod.OPTIONS).permitAll(): O parâmetro H�pMethod.OPTIONS permite que o cliente (frontend), possa descobrir quais são as opções permi�das em uma requisição, para um determinado recurso em um servidor. Nesta implementação, está sendo liberada todas as opções das requisições através do método .permitAll(). .anyRequest().authen�cated(): informa ao sistema que todas as demais rotas que não es�verem especificadas no escopo do authorizeRequests(), deverão ser auten�cadas. .h�pBasic(): informa ao sistema que o servidor irá receber requisições que devem ter o esquema HTTP Basic de auten�cação. Desta forma habilitamos o servidor para que as requisições tenham um formato específico de auten�cação. .sessionManagement().sessionCrea�onPolicy(SessionCrea�onPolicy.STATELESS): define que o nosso sistema não guardará sessões para o cliente. Quando o cliente faz uma solicitação HTTP, ele inclui todas as informações necessárias para o servidor atender à solicitação. O servidor nunca dependerá de informações de solicitações anteriores do cliente. Se alguma dessas informações for importante, o cliente a enviará novamente a informação como parte da solicitação atual. .cors(): libera o acesso de outras origens, desta forma nossa API poderá ser acessada de outros domínios, ou seja, de outros servidores, além do servidor onde a aplicação está hospedada. Exemplo: .csrf().disable(): desabilita a proteção que vem a�va contra o ataque do �po CSRF (Cross-Site-Request-Forgery), que seria uma interceptação dos dados de auten�cação por um Hacker. Esta configuração foi desabilitada porquê o Spring Security fica procurando por um parâmetro oculto adicional em qualquer requisição POST / PUT / DELETE, chamado token CSRF. Como ele não vai encontrar, apenas as requisiçõesdo �po GET seriam aceitas. Para mais detalhes sobre esta definição, o mais correto é olhar sua fonte no link abaixo: Documentação: H�pSecurity Documentação: HTTP Status Code 401 - Unauthorized Documentação: CORS Ar�go: Complete Guide to CSRF/XSRF (Cross-Site Request Forgery) Código fonte: Camada Security https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/builders/HttpSecurity.html https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Methods/OPTIONS https://developer.mozilla.org/pt-BR/docs/Web/HTTP/CORS https://reflectoring.io/complete-guide-to-csrf/ https://github.com/conteudoGeneration/blog-pessoal/tree/15-Blog-(Security_UserDetailsService_And_UserRepository) Projeto 02 - Blog Pessoal - Spring Security 03 O que veremos por aqui: 1. A Classe UsuarioService 2. A Classe UsuarioController 3. Atualização da Classe PostagemController 4. Testes no Postman Vamos finalizar o Ecossistema do Usuário. 👣 Passo 01 - Criar o Pacote Service 1. Na Guia Package explorer, clique com o botão direito do mouse sobre a Package com.genera�on.blogpessoal, na Source Folder src/main/java e clique na opção New 🡪 Package. 2. Na janela New Java Package, no item Name, acrescente no final do nome da Package .service e clique no botão Finish para concluir. 👣 Passo 02 - Criar a Classe UsuarioService na Camada Service A Classe UsuarioService é responsável por manipular as regras de negócio de usuário no sistema. Esta classe deve ser anotada com a anotação @Service, para que o Spring iden�fique que é uma classe que serviço e carregue ela sempre que puder. Vale mencionar que alguns métodos definidos abaixo são de extrema importância para o sistema. 1. Clique com o botão direito do mouse sobre o Pacote Security (com.genera�on.blogpessoal.service), na Source Folder Principal (src/main/java), e clique na opção New 🡪 Class 2. Na janela New Java Class, no item Name, digite o nome da Classe (UsuarioService), e na sequência clique no botão Finish para concluir. A seguir veja sua implementação: package com.generation.blogpessoal.service; import java.nio.charset.Charset; import java.util.Optional; import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; import com.generation.blogpessoal.model.Usuario; import com.generation.blogpessoal.model.UsuarioLogin; import com.generation.blogpessoal.repository.UsuarioRepository; @Service public class UsuarioService { @Autowired private UsuarioRepository usuarioRepository; public Optional<Usuario> cadastrarUsuario(Usuario usuario) { if (usuarioRepository.findByUsuario(usuario.getUsuario()) .isPresent()) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Usuário já existe!", null); usuario.setSenha(criptografarSenha(usuario.getSenha())); return Optional.of(usuarioRepository.save(usuario)); } public Optional<Usuario> atualizarUsuario(Usuario usuario) { if (usuarioRepository.findById(usuario.getId()).isPresent()) { Optional<Usuario> buscaUsuario = usuarioRepository. findByUsuario(usuario.getUsuario()); if (buscaUsuario.isPresent()) { if (buscaUsuario.get().getId() != usuario.getId()) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Usuário já existe!", null); } usuario.setSenha(criptografarSenha(usuario.getSenha())); return Optional.of(usuarioRepository.save(usuario)); } throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Usuário não encontrado!", null); } public Optional<UsuarioLogin> logarUsuario( Optional<UsuarioLogin> usuarioLogin) { Optional<Usuario> usuario = usuarioRepository .findByUsuario(usuarioLogin.get().getUsuario()); if (usuario.isPresent()) { if (compararSenhas(usuarioLogin.get().getSenha(), usuario.get().getSenha())) { usuarioLogin.get().setId(usuario.get().getId()); usuarioLogin.get().setNome(usuario.get().getNome()); usuarioLogin.get().setFoto(usuario.get().getFoto()); Observe que foram criados os Métodos cadastrarUsuario() e atualizarUsuario(), que criptografam a senha e impedem a duplicação do usuário no Banco de dados. O Método logarUsuario(), além de auten�car o usuário no sistema, ele compara a senha digitada pelo usuário com a senha persis�da no Banco de dados e gera o Token do usuário. usuarioLogin.get().setToken( generatorBasicToken(usuarioLogin.get().getUsuario(), usuarioLogin.get().getSenha())); usuarioLogin.get().setSenha(usuario.get().getSenha()); return usuarioLogin; } } throw new ResponseStatusException( HttpStatus.UNAUTHORIZED, "Usuário ou senha inválidos!", null); } private String criptografarSenha(String senha) { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String senhaEncoder = encoder.encode(senha); return senhaEncoder; } private boolean compararSenhas(String senhaDigitada, String senhaBanco) { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); return encoder.matches(senhaDigitada, senhaBanco); } private String generatorBasicToken(String email, String password) { String structure = email + ":" + password; byte[] structureBase64 = Base64 .encodeBase64(structure.getBytes(Charset.forName("US-ASCII"))); return "Basic " + new String(structureBase64); } } Para executar as operações: Criptografar Senha, Comparar Senhas e Gerar Token foram criados os respec�vos Métodos auxiliares na própria Classe UsuarioService. ALERTA DE BSM: Mantenha a Atenção aos Detalhes, o cadastro de um novo usuário no sistema necessita ser validado no Banco de dados. Caso o usuário já exista, a aplicação não deve permi�r que ele seja criado novamente, pois um usuário duplicado no sistema ocasionará um erro HTTP Status 500. ALERTA DE BSM: Mantenha a Atenção aos Detalhes, ao atualizar um usuário é importante que seja validado novamente a criptografia da senha e o usuário (e-mail). Caso não seja validado ocasionará um problema ao tentar pegar as credenciais pelo front-end da aplicação. ALERTA DE BSM: Mantenha a Atenção aos Detalhes, ao u�lizar o método para logarUsuario, se atentar de que seja passado todos os parâmetros do usuarioLogin, pois o mesmo será u�lizado pelo front- end da aplicação. Para concluir, não esqueça de Salvar o código (File 🡪 Save All). Documentação: @Service Documentação: BCryptPasswordEncoder - Java Doc Spring Documentação: Throw Documentação: Métodos de Referência (::) Documentação: Base64 - Java Doc Commons Código fonte: Classe UsuarioService https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Service.html https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.html https://docs.oracle.com/javase/tutorial/essential/exceptions/throwing.html https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html https://commons.apache.org/proper/commons-codec/apidocs/index.html https://github.com/conteudoGeneration/blog-pessoal/blob/16-Blog-(Security_UserService)/blogpessoal/src/main/java/com/generation/blogpessoal/service/UsuarioService.java 👣 Passo 03 - Criar a Classe UsuarioController na Camada Controller A classe UsuarioController, é responsável por fornecer o acesso aos recursos do sistema. Uma das suas funcionalidades abaixo é promover o CRUD do usuário. Além de permi�r total manipulação do usuário, consumindo os serviços cadastrar usuário, atualizar usuário e auten�car da Classe UsuarioService. 1. Clique com o botãodireito do mouse sobre o Pacote Security (com.genera�on.blogpessoal.controller), na Source Folder Principal (src/main/java), e clique na opção New 🡪 Class 2. Na janela New Java Class, no item Name, digite o nome da Classe (UsuarioController), e na sequência clique no botão Finish para concluir. A seguir veja sua implementação: package com.generation.blogpessoal.controller; import java.util.List; import java.util.Optional; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.generation.blogpessoal.model.Usuario; import com.generation.blogpessoal.model.UsuarioLogin; import com.generation.blogpessoal.repository.UsuarioRepository; import com.generation.blogpessoal.service.UsuarioService; @RestController @RequestMapping("/usuarios") @CrossOrigin(origins = "*", allowedHeaders = "*") public class UsuarioController { @Autowired private UsuarioService service; @Autowired private UsuarioRepository repository; @GetMapping("/all") public ResponseEntity <List<Usuario>> getAll() { return ResponseEntity.ok(repository.findAll()); } @GetMapping("/{id}") public ResponseEntity<Usuario> getById(@PathVariable long id) { return repository.findById(id) .map(resp -> ResponseEntity.ok(resp)) .orElse(ResponseEntity.notFound().build()); } @PostMapping("/logar") public ResponseEntity<UsuarioLogin> autenticationUsuario( @RequestBody Optional<UsuarioLogin> usuario) { return service.logarUsuario(usuario) .map(resp -> ResponseEntity.ok(resp)) .orElse(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()); } @PostMapping("/cadastrar") public ResponseEntity<Usuario> postUsuario( @Valid @RequestBody Usuario usuario) { return service.cadastrarUsuario(usuario) .map(resp -> ResponseEntity.status(HttpStatus.CREATED).body(resp)) .orElse(ResponseEntity.status(HttpStatus.BAD_REQUEST).build()); } @PutMapping("/atualizar") public ResponseEntity<Usuario> putUsuario( @Valid @RequestBody Usuario usuario){ return service.atualizarUsuario(usuario) .map(resp -> ResponseEntity.status(HttpStatus.OK).body(resp)) .orElse(ResponseEntity.status(HttpStatus.NOT_FOUND).build()); } } Observe que nos Métodos Cadastrar, Atualizar e Auten�car, ao invés de usarmos a injeção de dependência da Interface UsuarioRepository, estamos u�lizando a injeção de dependência da Classe de Serviço UsuarioService porquê os 3 Métodos foram implementados nela. ALERTA DE BSM: Mantenha a Atenção aos Detalhes, nos métodos postUsuario e putUsuario para não esquecer a notação @Valid. Caso ela não seja inserida, ao fornecer parâmetros inválidos, o servidor retornará o HTTP Status 500, ao invés do HTTP Status 400. Para concluir, não esqueça de Salvar o código (File 🡪 Save All). Código fonte: Classe UsuarioController 👣 Passo 04 - Testar o Recurso Usuario no Postman Vamos criar no Postman todas as requisições necessárias para testar os 5 Métodos do Recurso Usuario. Veja abaixo como ficam as requisições para testar o Recurso Usuario: DICA: Caso você tenha alguma dúvida sobre como criar as Requisições, consulte a Documentação dos Recursos Postagem e Tema. ATENÇÃO: Depois de criar o Relacionamento entre Classes, todas as Consultas dos Recursos Postagem, Tema e Usuario trarão os Objetos associados. 4.1. Criando a Pasta Usuario Vamos criar dentro da Collec�on Blog Pessoal a Pasta Usuario, que guardará todas as requisições do Recurso Usuario. 1. Na Collec�on Blog Pessoal, clique nos 3 pon�nhos ao lado do nome da Collec�on, para abrir o menu e clique na opção Add Folder. 2. Na janela que será aberta, informe o nome da pasta (Usuario) e pressione a tecla (Enter) para concluir. https://github.com/conteudoGeneration/blog-pessoal/blob/17-Blog-(Security_UserController)/blogpessoal/src/main/java/com/generation/blogpessoal/controller/UsuarioController.java 4.2. Criando a Request - Cadastrar Usuario Vamos começar pela requisição Cadastrar Usuario porquê sem um usuário cadastrado não será possível auten�car (logar) no sistema e acessar os demais endpoints. 1. Na Pasta Usuario, clique nos 3 pon�nhos ao lado do nome da pasta, para abrir o menu e clique na opção Add Request. 2. Configure a requisição conforme a imagem abaixo: 3. Observe que na requisição do �po Post o Corpo da requisição (Request Body), deve ser preenchido com um JSON contendo o nome, o usuario(e-mail), a senha e a foto(link), que vc deseja persis�r no Banco de dados. 4. Observe que depois de persis�r os dados do usuario, a aplicação retornará a senha criptografada. 4.3. Criando a Request - Auten�car Usuario (Logar) Usuário persis�do, agora vamos efetuar o login no sistema, que nos retornará o Token de Autorização, que nos permi�rá consumir os demais endpoints e recursos da aplicação. 1. Na Pasta Usuario, clique nos 3 pon�nhos ao lado do nome da pasta, para abrir o menu e clique na opção Add Request. 2. Configure a requisição conforme a imagem abaixo: 3. Observe que no JSON estamos passando apenas o usuário e a senha, porquê são as únicas informações que u�lizaremos no login. A Classe UsuarioLogin possui outros atributos que serão preenchidos pela aplicação (se o login for efetuado com sucesso) e retornados no JSON da resposta, como vemos na figura abaixo: 4. Observe que o Token foi gerado e enviado na resposta. Copie o Token da resposta porquê vamos precisar dele nas próximas requisições. 5. Caso o login falhe, você receberá o status 401 (Unauthorized). 4.4. Criando a Request - Consultar todos os Usuarios - findAll() 1. Na Pasta Usuario, clique nos 3 pon�nhos ao lado do nome da pasta, para abrir o menu e clique na opção Add Request. 2. Configure a requisição conforme a imagem abaixo: 3. Como a segurança foi habilitada, caso você envie a requisição do jeito que está, você receberá o status 401 (Unauthorized), como mostra a figura abaixo: 4. A explicação é simples: Você precisa passar no cabeçalho da requisição o Token recebido no login. 5. Clique na opção Headers e adicione a linha Authoriza�on. Na coluna Value da linha insira o Token que você recebeu no Login, como mostra a figura abaixo: 6. Agora a sua requisição funcionará! ALERTA DE BSM: Mantenha a Atenção aos Detalhes! Você deverá inserir o token no cabeçalho de todas requisições dos Recursos Postagem e Tema, caso contrário você receberá o status 401 (Unauthorized) em todas elas. Caso você efetue login com outro Usuário, o Token deve ser alterado. 4.5. Criando a Request - Consultar Usuario por ID - findById(id) 1. Na Pasta Usuario, clique nos 3 pon�nhos ao lado do nome da pasta, para abrir o menu e clique na opção Add Request. 2. Configure a requisição conforme a imagem abaixo: 3. Clique na opção Headers e adicione a linha Authoriza�on. Na coluna Value da linha insira o Token que você recebeu no Login, como mostra a figura abaixo: 4.6. Criando a Request - Atualizar Usuário 1. Na Pasta Usuario, clique nos 3 pon�nhos ao lado do nome da pasta, para abrir o menu e clique na opção Add Request. 2. Configure a requisição conforme a imagem abaixo: 3. Observe que na requisição do �po Put o Corpo da requisição (Request Body), deve ser preenchido com um JSON contendo o Id, o nome, o usuario(e-mail), a senha e a foto(link) que vc deseja atualizar no Banco de dados. 4. Clique na opção Headers e adicione alinha Authoriza�on. Na coluna Value da linha insira o Token que você recebeu no Login, como mostra a figura abaixo: 👣 Passo 05 - Atualizar as Requisições Cadastrar e Atualizar Postagem no Postman Como habilitamos o Relacionamento entre as Classes Postagem e Usuario, para Cadastrarmos e Alterarmos as Postagens vamos precisar atender alguns requisitos: O Tema e o Usuario devem ser persis�dos antes de criar a nova Postagem. Na requisição Cadastrar e Atualizar Postagem, o JSON enviado no Corpo da Requisição deve conter um Objeto da Classe Tema iden�ficado apenas pelo Atributo id e um Objeto da Classe Usuario iden�ficado apenas pelo Atributo id. 5.1. Atualização - Requisição Cadastrar Postagem Vamos alterar o Corpo da requisição (Body), conforme a imagem abaixo: No item marcado em vermelho na imagem acima, observe que está sendo passado dentro do JSON um Objeto da Classe Usuario chamado usuario, iden�ficado apenas pelo Atributo id. Não esqueça de inserir o token no cabeçalho da requisição. ATENÇÃO: O Objeto Usuario deve ser persis�do no Banco de dados antes de ser inserido no JSON da requisição Cadastrar Postagem. 5.2. Atualização - Requisição Atualizar Postagem Vamos alterar o Corpo da requisição (Body), conforme a imagem abaixo: No item marcado em vermelho na imagem acima, observe que está sendo passado dentro do JSON um Objeto da Classe Usuario chamado usuario, iden�ficado apenas pelo Atributo id. Não esqueça de inserir o token no cabeçalho da requisição. ATENÇÃO: O Objeto Usuario deve ser persis�do no Banco de dados antes de ser inserido no JSON da requisição Atualizar Postagem. DESAFIO: O que acontecerá se você inserir no JSON das requisições Cadastrar e Atualizar Postagem, no Objeto da Classe Usuario chamado usuario, um id que não existe? Insira no Atributo id, do Objeto Usuario, um id como 100, por exemplo, e veja o que acontece. 👣 Passo 06 - Atualizar os Métodos post e put na Classe PostagemController Se você fez o desafio acima, percebeu que estas implementações não conseguem checar se o Objeto da Classe Usuario existe, logo se você inserir um Objeto que não existe (um Id que não existe no Banco de dados), devido ao Relacionamento entre as Classes, será retornado o HTTP Status 500 - Internal Server Error. Para evitar este erro, faremos alguns ajustes na Classe PostagemController. 6.1. Inserir uma Injeção de Dependência da Classe Usuario na Classe PostagemController Linhas 44 e 45: Para termos acesso aos Métodos das Classes Usuario e UsuarioController, precisamos inserir uma uma Injeção de Dependência da Classe Usuario, logo abaixo da Injeção de Dependência da Classe Tema. 6.2. Atualização do Método post da Classe PostagemController Linha 67: Através do Método existsById(Long id), da Interface UsuarioRepository (Herança da Interface JPA), checamos se o id passado no Objeto usuario, da Classe Usuario, inserido no Objeto postagem, da Classe Postagem, existe, assim como fizemos na checagem da Classe Tema. Se ambos os Objetos exis�rem, a nova postagem será persis�da no Banco de dados. Para obter o id do usuario, u�lizamos os Métodos get das 2 Classes: postagem.getUsuario().getId() Documentação: existsById() 6.3. Atualização do Método post da Classe PostagemController Linha 80: Através do Método existsById(Long id), da Interface UsuarioRepository (Herança da Interface JPA), checamos se o id passado no Objeto usuario, da Classe Usuario, inserido no Objeto postagem, da Classe Postagem, existe, assim como fizemos na checagem da Classe Tema. Se ambos os Objetos exis�rem, a nova postagem será persis�da no Banco de dados. Para obter o id do usuario, u�lizamos os Métodos get das 2 Classes: postagem.getUsuario().getId() Código fonte do projeto Segurança do Blog Pessoal finalizada!!! https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html#existsById-ID- https://github.com/conteudoGeneration/blog-pessoal/tree/17-Blog-(Security_UserController)
Compartilhar