Implementação
Agora vamos ver como podemos implementar o mecanismo de login e salvamento do JWT em um aplicativo Spring real.
Dependências
Você pode ver a lista de dependências do Maven que nosso código de exemplo usa abaixo. Observe que as dependências principais como Spring Boot e Hibernate não estão incluídas nesta captura de tela.
Salvando usuários
Começaremos criando controladores para salvar usuários com segurança e autenticá-los com base em nome de usuário e senha.
Temos uma entidade modelo chamada User. É uma classe de entidade simples que mapeia para o DO UTILIZADOR tabela. Você pode usar as propriedades de que precisa, dependendo do seu aplicativo.
Nós também temos um simples UserRepository classe para salvar usuários. Precisamos substituir o findByUsername já que iremos usá-lo na autenticação.
public interface UserRepository extends JpaRepository<User, String>{ User findByUsername(String username); }
Nunca devemos armazenar senhas de texto simples no banco de dados porque muitos usuários tendem a usar a mesma senha para vários sites.
Existem muitos algoritmos de hash diferentes, mas o mais comumente usado é BCrypt e é um método recomendado de hash seguro. Você pode verificar isto artigo para mais informações sobre o assunto.
@Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); }
Para hash a senha, vamos definir um BCrypt feijão em @SpringBootApplication e anote a classe principal da seguinte forma:
Chamaremos os métodos neste bean quando precisarmos fazer o hash de uma senha.
Também precisamos de um UserController para salvar os usuários. Criamos o controlador, anotamos com @RestController, e definir o mapeamento correspondente.
Em nosso aplicativo, salvamos o usuário com base em um objeto DTO que é passado pelo front end. Você também pode passar um objeto de usuário em @RequestBody.
Depois de passar o objeto DTO, criptografamos o campo de senha usando o BCrypt bean que criamos anteriormente. Você também pode fazer isso no controlador, mas é uma prática melhor colocar essa lógica na classe de serviço.
@Transactional(rollbackFor = Exception.class)public String saveDto(UserDto userDto) { userDto.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword())); return save(new User(userDto)).getId(); }
Filtro de Autenticação
Precisamos de autenticação para garantir que o usuário seja realmente quem afirma ser. Estaremos usando o par clássico de nome de usuário / senha para fazer isso.
Aqui estão as etapas para implementar a autenticação:
- Crie nosso filtro de autenticação que estende UsernamePasswordAuthenticationFilter
- Crie uma classe de configuração de segurança que estende WebSecurityConfigurerAdapter e aplique o filtro
Aqui está o código para nosso filtro de autenticação – como você deve saber, os filtros são a espinha dorsal do Spring Security.
Vamos examinar esse código passo a passo.
Esta classe estende UsernamePasswordAuthenticationFilter que é a classe padrão para autenticação de senha no Spring Security. Nós o estendemos para definir nossa lógica de autenticação personalizada.
Fazemos uma chamada para o setFilterProcessesUrl método em nosso construtor. Este método define o URL de login padrão para o parâmetro fornecido.
Se você remover esta linha, Spring Security cria o “/Conecte-se” endpoint por padrão. Ele define o ponto de extremidade de login para nós, e é por isso que não definiremos um ponto de extremidade de login em nosso controlador explicitamente.
Após esta linha, nosso endpoint de login será / api / services / controller / user / login. Você pode usar esta função para ficar consistente com seus terminais.
Nós substituímos o tryAuthentication e bem-sucedidoAuthentication métodos do UsernameAuthenticationFilter classe.
o tryAuthentication A função é executada quando o usuário tenta fazer login em nosso aplicativo. Ele lê as credenciais, cria um POJO do usuário a partir delas e, em seguida, verifica as credenciais para autenticar.
Passamos o nome de usuário, a senha e uma lista vazia. A lista vazia representa as autoridades (funções) e a deixamos como está, pois ainda não temos funções em nosso aplicativo.
Se a autenticação for bem-sucedida, o bem-sucedidoAuthentication método é executado. Os parâmetros desse método são passados pelo Spring Security nos bastidores.
o tryAuthentication método retorna um Autenticação objeto que contém as autoridades pelas quais passamos durante a tentativa.
Queremos retornar um token para o usuário após a autenticação ser bem-sucedida, portanto, criamos o token usando nome de usuário, segredo e data de expiração. Precisamos definir o SEGREDO e DATA DE VALIDADE agora.
Criamos uma classe para ser um contêiner para nossas constantes. Você pode definir o segredo como quiser, mas a prática recomendada é tornar a chave secreta tão longa quanto o seu hash. Nós usamos o HS256 algoritmo neste exemplo, então nossa chave secreta é 256 bits / 32 caracteres.
O tempo de expiração é definido para 15 minutos, porque é a melhor prática contra ataques de força bruta de chave secreta. O tempo está em milissegundos.
Preparamos nosso filtro de autenticação, mas ele ainda não está ativo. Também precisamos de um filtro de autorização e, em seguida, aplicaremos os dois por meio de uma classe de configuração.
Este filtro verificará a existência e validade do token de acesso no cabeçalho de autorização. Especificaremos quais endpoints estarão sujeitos a este filtro em nossa classe de configuração.
Filtro de Autorização
o doFilterInternal método intercepta as solicitações e verifica o cabeçalho de autorização. Se o cabeçalho não estiver presente ou não começar com “BEARER”, ele segue para a cadeia de filtros.
Se o cabeçalho estiver presente, o getAuthentication método é invocado. getAuthentication verifica o JWT e, se o token for válido, ele retorna um token de acesso que o Spring usará internamente.
Esse novo token é então salvo no SecurityContext. Você também pode passar Autoridades para este token se precisar de autorização baseada em função.
Nossos filtros estão prontos e agora precisamos colocá-los em ação com a ajuda de uma classe de configuração.
Configuração
Nós anotamos esta aula com @EnableWebSecurity e estender WebSecurityConfigureAdapter para implementar nossa lógica de segurança personalizada.
Autowire o bean BCrypt que definimos anteriormente. Nós também autowire o UserDetailsService para encontrar a conta do usuário.
O método mais importante é aquele que aceita um HttpSecurity objeto. Aqui especificamos os pontos de extremidade e filtros seguros que queremos aplicar. Configuramos o CORS e, em seguida, permitimos todas as solicitações de postagem para nossa URL de inscrição que definimos na classe constantes.
Você pode adicionar outros matchers de formigas para filtrar com base em padrões de URL e funções, e você pode Verifica esta questão StackOverflow para exemplos a respeito. O outro método configura o AuthenticationManager para usar nosso objeto codificador como seu codificador de senha ao verificar as credenciais.
Testando
Vamos enviar alguns pedidos para testar se funciona corretamente.
Aqui, enviamos uma solicitação GET para acessar um recurso protegido. Nosso servidor responde com um código 403. Este é o comportamento esperado porque não fornecemos um token no cabeçalho. Agora vamos criar um usuário:
Para criar um usuário, enviamos uma solicitação de postagem com nossos dados de DTO do usuário. Usaremos esse usuário para fazer login e obter um token de acesso.
Ótimo! Pegamos o token. Após este ponto, usaremos esse token para acessar recursos protegidos.
Fornecemos o token no cabeçalho de autorização e agora temos permissão para acessar nosso endpoint protegido.
Conclusão
Neste tutorial, acompanhei você nas etapas que executei ao implementar a autorização JWT e a autenticação de senha no Spring. Também aprendemos como salvar um usuário com segurança.
Obrigado por ler – espero que tenha sido útil para você. Se você estiver interessado em ler mais conteúdo como este, fique à vontade para se inscrever no meu blog em https://erinc.io. 🙂