Opa, boa tarde!
Estou concluindo o curso e estou nas versões atuais do Spring, diferente do projeto. Eu consigo recuperar o token no /login, mas ao tentar acessar outra requisição mesmo com o token no cabeçalho, ele retorna 403 forbidden.
SecurityConfigurations:
@Configuration
@EnableWebSecurity //personalizar as config de segurança
public class SecurityConfigurations {
@Autowired
private SecurityFilter securityFilter;
@Bean //devolve um objeto para o spring
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
//stateless é um tipo de estado do servidor, ele não armazena nenhuma memória no programa
return http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(req -> {
req.requestMatchers("/login").permitAll();
req.anyRequest().authenticated();
})
.addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean //serve para ensinar o spring a injetar um objeto (authenticationManager) que vamos utilizar internamente
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean //config para hash de senha
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
TokenService:
//Classe de validação e geração do token
@Service
public class TokenService {
@Value("${api.security.token.secret}")
private String secret;
private static final String ISSUER = "API Voll.med";
public String gerarToken(UsuarioEntity usuario) {
try {
var algoritmo = Algorithm.HMAC256(secret);
return JWT.create()
//issuer -> o "nome" da assinatura do token
.withIssuer(ISSUER)
//subject -> usuário que é responsavel pelo token
.withSubject(usuario.getLogin())
//expiresAt -> método para expiração do token
.withExpiresAt(dataExpiracao())
.sign(algoritmo);
} catch (JWTCreationException exception){
throw new RuntimeException("Erro ao gerar o token JWT!", exception);
}
}
public String validarToken(String tokenJWT) {
try {
var algoritmo = Algorithm.HMAC256(secret);
return JWT.require(algoritmo)
.withIssuer(ISSUER)
.build()
//Verifica se o token está válido de acordo com o algoritmo e o issuer
.verify(tokenJWT)
.getSubject();
} catch (JWTVerificationException exception) {
throw new RuntimeException("Token JWT inválido ou expirado!");
}
}
private Instant dataExpiracao() {
return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-03:00"));
}
}
SecurityFilter:
//Herdamos uma classe do Spring para fazer o filtro
@Component
public class SecurityFilter extends OncePerRequestFilter {
@Autowired
private TokenService tokenService;
@Autowired
private UsuarioRepository repository;
//Será executada a cada uma vez pra cada requisição
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//Código para recuperar e validar o token
var tokenJWT = recuperarToken(request);
if (tokenJWT.isEmpty()) {
var subject = tokenService.validarToken(tokenJWT);
var usuario = repository.findByLogin(subject);
var authentication = new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
//Chama o próximo filtro para seguir o fluxo da aplicação
filterChain.doFilter(request, response);
}
private String recuperarToken(HttpServletRequest request) {
//Recupera o cabeçalho authorization onde está o token
var authorizationHeader = request.getHeader("Authorization");
if(authorizationHeader != null) {
return authorizationHeader.replace("Bearer", "");
}
return "Cabeçalho Authorization vazio ou nulo!";
}
}
Desde já, muito obrigado!