Alura > Cursos de Programação > Cursos de Java > Conteúdos de Java > Primeiras aulas do curso Java e Spring Security: crie perfis e autorize requisições

Java e Spring Security: crie perfis e autorize requisições

Criando diferentes tipos de usuários - Apresentação

Olá, boas-vindas a mais um curso de Segurança em Java aqui na Alura. Eu sou Iasmin Araújo, faço parte da Escola de Programação e, para fins de acessibilidade, vou me autodescrever.

Audiodescrição: Iasmin se autodeclara uma mulher branca com cabelos castanhos escuros, lisos e longos, vestindo uma camiseta rosa. Ao fundo, há uma parede iluminada com luz azul.

Neste curso, continuaremos a evoluir nossos conhecimentos sobre segurança. Anteriormente, falamos bastante sobre autenticação, e agora o foco será na autorização, na criação de vários perfis diferentes com diversas permissões. Continuaremos trabalhando na evolução do projeto da Clínica Vollmed.

Começaremos transformando as entidades da aplicação em usuários: médicos e pacientes da Clínica Vollmed conseguirão acessar a aplicação a partir de uma conta com login. Também aprenderemos a criar diferentes perfis de acesso, e médicos, pacientes e atendentes previamente existentes terão diferentes perfis e permissões. Por exemplo, o atendente terá maior privilégio em relação ao médico e ao paciente.

Para facilitar o gerenciamento desses perfis, aprenderemos a autorizar as requisições com o Spring Security, por meio de várias abordagens diferentes. Também criaremos diferentes visualizações para cada perfil. Com o Thymeleaf no front-end, trataremos as diferentes visualizações de acordo com as permissões de cada perfil.

Aprenderemos a alterar a senha de um usuário e a desenvolver a funcionalidade de recuperação de senha. Durante o estudo, também entenderemos como enviar e-mails para os usuários com base nos dados já armazenados.

Este curso é para quem está acompanhando a formação de segurança com Java e já concluiu o primeiro curso, tendo dado os primeiros passos com Java e Spring Security. É importante ter conhecimento em Spring Boot e banco de dados para tratar a aplicação de forma completa, utilizando também as consultas dos bancos de dados.

Lembre-se de fazer os exercícios do curso e de consultar todos os materiais complementares. Dê atenção especial aos nossos "mão-na-massa", pois neles há várias funcionalidades que podemos incrementar na aplicação, tornando-a mais completa. Assim, você conseguirá consolidar seus conhecimentos de forma mais eficaz.

Se tiver alguma dúvida, lembre-se de que pode deixá-la no fórum ou no Discord da Alura, onde nossa comunidade poderá ajudar. Com todos os recados dados, vamos começar o desenvolvimento do nosso curso!

Criando diferentes tipos de usuários - Transformando médicos em usuários

Continuaremos trabalhando com o projeto da clínica Vollmed. Para relembrar o que foi feito anteriormente, vamos abrir localhost:8080 no navegador, em que teremos a nossa página inicial. Tínhamos os usuários João e Maria; vamos clicar no link de login localizado no canto superior direito da aplicação e entrar com o usuário do João, cujo e-mail é joao.email.com, e a senha é joao123. Feito o login, a mensagem "Olá, João!" é exibida na tela, no canto superior direito junto à opção de sair, onde antes estava a área de login.

Anteriormente, na parte de cadastros, tínhamos apenas as áres referentes a médicos e consultas. Agora, também temos uma aba de pacientes. É possível listar os pacientes e realizar cadastros incluindo nome, e-mail, telefone e CPF. Isso porque, anteriormente, nosso foco estava nos atendentes e nos dados dos médicos, com CRM e dados específicos. Para expandir nossa aplicação, além dos dados dos médicos, adicionaremos os dados dos pacientes, pois queremos transformar médicos e pacientes em usuários.

Vamos abrir o projeto no IntelliJ. Na pasta do projeto, temos um novo pacote "paciente", que contém várias classes relacionadas a dados. Também temos uma classe PacienteController na pasta "controller". Nas migrations, adicionamos a criação de uma tabela de pacientes e alteramos a tabela de consultas, que agora possui uma chave estrangeira (foreign key) de médico e uma chave estrangeira de paciente.

Nosso objetivo é transformar médicos e pacientes em usuários, e existem várias formas de fazê-lo. Vamos começar pela classe Medico: temos nome, e-mail, telefone, CRM e especialidade. Enquanto isso, o Usuario possui apenas nome, e-mail e senha. Podemos reaproveitar os dados de nome e e-mail do médico para salvar o usuário também. Na classe MedicoService, no método cadastrar(), vemos que ele recebe um DadosCadastroMedico e cria uma entidade do tipo Medico.

Queremos transformar médicos em usuários sem deixar de salvar o médico no banco. Uma estratégia é, ao salvar o médico, criar uma entidade para representar o usuário com os dados necessários para criá-lo. Para isso, precisaremos da classe UsuarioService dentro de MedicoService. Após o private final MedicoRepository, declaramos um private final UsuarioService, chamando-o de usuarioService. O IntelliJ reclamará, pois precisamos inicializar essa variável. Vamos usar "Alt + Enter" e adicionar um parâmetro ao construtor, que será gerado automaticamente:

public MedicoService(MedicoRepository repository, UsuarioService usuarioService) {
    this.repository = repository;
    this.usuarioService = usuarioService;
}

Assim, ao salvar o médico, também salvamos um usuário com os mesmos dados. Vamos usar usuarioService.salvarUsuario, criando um método responsável por transformar os dados do médico em um usuário. Usaremos essa lógica para pacientes também, justificando a criação de um método específico. Para salvar um usuário, precisamos de nome, e-mail e senha. Usaremos o CRM como senha inicial, que deverá ser alterada posteriormente para garantir segurança.

if (dados.id() == null) {
    repository.save(new Medico(dados));
    usuarioService.salvarUsuario(dados.nome(), dados.email(), dados.crm());
} else {
    //trecho de código omitido
}

Criamos um esboço do método salvarUsuario que ainda não existe na classe UsuarioService. Podemos criá-lo clicando sobre o método e usando "Alt + Enter" para selecionar "Create method 'salvarUsuario' in 'UsuarioService'". Este será um método void com nome, e-mail e senha como parâmetros. Baseado nisso, construiremos um usuário passando nome, e-mail e senha.

public void salvarUsuario(String nome, String email, String senha) {
    usuarioRepository.save(new Usuario(nome, email, senha));
}

Este método também não existe na classe Usuario, então precisaremos criá-lo. Após a declaração de todos os atributos, criaremos um construtor usando "Alt + Insert", selecionando nome, e-mail e senha. Criaremos um construtor padrão, necessário para o JPA trabalhar com as entidades:

private Usuario() {}

public Usuario(String nome, String email, String senha) {
    this.nome = nome;
    this.email = email;
    this.senha = senha;
}

Na classe UsuarioService, será necessário criptografar a senha antes de salvá-la, e o Spring Security consiguirá compará-la com a senha salva no banco de dados. Para isso, usaremos um passwordEncoder, torando a variável final para que possamos injetá-la no construtor por meio do atalho "Alt + Enter" e seleção de "Add constructor parameter".

private final PasswordEncoder encriptador;

Ao declararmos de forma abstrata, o próprio Spring Security entenderá que estamos nos referindo ao passwordEncoder configurado na classe de segurança. Criaremos uma variável senhaCriptografada usando encriptador.encode(senha), passando a senha recebida como parâmetro. No construtor, usaremos senhaCriptografada em vez da senha original. Assim, conseguimos salvar um usuário com os dados do médico.

public void salvarUsuario(String nome, String email, String senha) {
    String senhaCriptografada = encriptador.encode(senha);
    usuarioRepository.save(new Usuario(nome, email, senhaCriptografada));
}

A seguir, testaremos o funcionamento, verificando o fluxo e analisando se tudo está correto.

Criando diferentes tipos de usuários - Sincronizando IDs de usuários

Para testar as alterações no projeto, no canto superior direito do IntelliJ, clicaremos no ícone de seta circular com um triângulo para reiniciar o projeto, e no botão "Stop and Rerun". Após a execução, abriremos o navegador e acessaremos a página inicial.

Então, acessaremos "Cadastros > Médicos" na parte superior da página, e clicaremos no botão "+ Novo Médico". Vamos criar um médico com dados fictícios: nome "Pedro", e-mail "pedro@email.com", telefone com vários números 9, CRM "555555" e especialidade "Ortopedia". Após inserir os dados, clicaremos "+ Cadastrar". Assim, teremos que o médico Pedro foi cadastrado com sucesso.

Vamos verificar, agora, se ele também foi cadastrado como usuário. Sairemos da conta do João e tentaremos logar com os dados do Pedro. Conseguiremos logar a partir do e-mail e do CRM, e a mensagem "Olá, Pedro" é exibida no canto superior direito. Isso confirma que o cadastro de médicos e usuários está correto.

Ao acessarmos a listagem de médicos e excluirmos Pedro em "Ações" e selecionando "Excluir", ele é removido da lista de médicos. Porém, ao relogarmos com "pedro@email.com" e a senha "555555", o acesso continua sendo possível. Isso não é o desejado, pois ao removermos um médico, o usuário correspondente também deveria ser deletado. As operações precisam ser sincronizadas, e isso não está acontecendo na aplicação.

Atualmente, configuramos apenas o momento de salvar um usuário junto com o médico, mas não configuramos a exclusão. Em MedicoService.java, recebemos um ID do médico e fazemos um deleteById(). Isto é, não fizemos nada em relação ao usuário. Precisaríamos olhar para os dados do usuário, pegando o e-mail, o único dado compartilhado entre usuário e médico e, então, deletaríamos. Isso, porém, demandaria muito trabalho e consultas no banco de dados, o que pode deixar a aplicação lenta.

Uma solução simples é compartilhar o ID de médico e usuário. Ao passar o ID na exclusão, buscamos o usuário, o que é mais rápido do que buscar pelo e-mail. Para implementar isso, precisamos fazer alterações no cadastro. Atualmente, o usuarioService e o médico são salvos separadamente, mas queremos que compartilhem o ID. Passaremos o ID do usuário cadastrado para o médico, pois o usuário é a parte mais genérica. Isso facilita futuras operações, como manipular pacientes.

Para isso, alteraremos a ordem de salvamento: primeiro salvaremos o usuário, depois o médico com o mesmo ID. Em MedicoService.java, moveremos a linha com usuarioService para que ela fique antes da linha com repository.save(). Além disso, para pegar o ID do usuário e passá-lo para o médico, criaremos uma variável id que será retornada pelo método salvarUsuario. Esse ID será passado no construtor de médico a ser atribuído na inicialização.

if (dados.id() == null) {
    Long id = usuarioService.salvarUsuario(dados.nome(), dados.email(), dados.crm());
    repository.save(new Medico(id, dados));
} else {
    var medico = repository.findById(dados.id()).orElseThrow(); medico.atualizarDados(dados);
}

Com "Ctrl + [clique]" sob salvarUsuario, acessaremos o método da classe UsuarioService, em que alteraremos para um Long em vez de void seja retornado com o ID do usuário. O método save() do repositório devolve o usuário salvo, permitindo retornar o ID com usuario.getId().

public Long salvarUsuario(String nome, String email, String senha) {
    String senhaCriptografada = encriptador.encode(senha);
    Usuario usuario = usuarioRepository.save(new Usuario(nome, email, senhaCriptografada));
    return usuario.getId();
}

Como getId não existe na classe Usuario, adicionaremos esse método após a linha contendo getNome, digitando getId e pressionando a tecla "Tab" para que o IntelliJ complete o código automaticamente. Em MedicoService.java, adicionaremos um Long id e os dados de cadastro no construtor de médico. Atribuiremos this.id = id; ao ID do parâmetro vindo do usuário.

public Medico(Long id, DadosCadastroMedico dados) {
    this.id = id;
    atualizarDados(dados);
}

Com essas alterações, precisaremos ajustar o banco de dados, pois o ID não será mais autoincrementável. Na sequência, trabalharemos nessa parte e testaremos a exclusão para garantir que tudo está funcionando conforme esperado.

Sobre o curso Java e Spring Security: crie perfis e autorize requisições

O curso Java e Spring Security: crie perfis e autorize requisições possui 184 minutos de vídeos, em um total de 57 atividades. Gostou? Conheça nossos outros cursos de Java em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Java acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas