Para criar o controle de acesso, todas as requisições devem enviar o token jwt e verificar a sua validade. Para que isso seja feito de maneira que não seja necessário repetir código no controller, é criado uma classe que é responsável por essa validação. Desta forma, a requisição é interceptada antes de chegar ao controller. Este interceptador é o Handler Interceptor do Spring, que funciona como um Filter da especificação Servlet.
A classe que representa o Filter é anotada com @Component, essa anotação é utilizada para que o Spring carregue uma classe genérica. Esta classe também deve herdar da classe OncePerRequestFilter do Spring e implementar o método doFilter().
Para que a requisição seja repassada para o próximo filtro, é necessário utilizar o método doFilter() do objeto FilterChain.
O Token é enviando no cabeçalho da requisição, por ser uma informação de configuração. Este cabeçalho é chamado de Authorization.
Para validar o Token:
public String getSubject(String tokenJWT){
try {
Algorithm algoritmo = Algorithm.HMAC256(secret);
return JWT.require(algoritmo)
.withIssuer("API Voll.med")
.build()
.verify(tokenJWT)
.getSubject();
} catch (JWTVerificationException exception){
throw new RuntimeException("Token JWT inválido ou expirado", exception);
}
}
Autenticação do usuário
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
var tokenJWT = recuperarToken(request);
if(tokenJWT != null){
var subject = tokenService.getSubject(tokenJWT);
var usuario = usuarioRepository.findByLogin(subject);
//força a autenticação
var authentication = new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request,response);
}
private String recuperarToken(HttpServletRequest request) {
var authorizationHeader = request.getHeader("Authorization");
if(authorizationHeader != null){
return authorizationHeader.replace("Bearer ","");
}
return null;
}
@Bean //devolver um objeto para o Spring
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
return http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests()
.requestMatchers(HttpMethod.POST, "/login").permitAll() //desabilita filtro para endpoint de login
.anyRequest().authenticated() //habilita para todos os outros
.and().addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
.build();
//desabilita proteçao contra ataques Cross-Site Request Forgery -> Token já faz isso
//desabilita Statefull
}
Filters
em uma requisição;OncePerRequestFilter
, do Spring;