Quando eu vou testar no navegador me manda pra tela de Login! e eu coloquei o SPRING_ACTIVE_PROFILE='prod' mas não resolve. A API está igual a do professor, eu até baixei para garantir que não tivesse nenhum erro.
Quando eu vou testar no navegador me manda pra tela de Login! e eu coloquei o SPRING_ACTIVE_PROFILE='prod' mas não resolve. A API está igual a do professor, eu até baixei para garantir que não tivesse nenhum erro.
Não está pegando o perfil como prod, mesmo eu passando SPRING_ACTIVE_PROFILES='prod'
$ docker run -p 8080:8080 -e FORUM_DATABASE_URL='jdbc:h2:mem:alura-forum' -e FORUM_DATABASE_USERNAME='sa' -e FORUM_DATABASE_PASSWORD='' -e FORUM_JWT_SECRET='123456' -e SPRING_ACTIVE_PROFILE='prod' versao/final
. __ _ __ _ _ /\ / _'_ _ _ _()_ _ __ _ \ \ \ ( ( )___ | ' | '| | ' / ` | \ \ \ \/ ___)| |)| | | | | || (| | ) ) ) ) ' |__| .|| ||_| |_, | / / / / =========||==============|__/=//// :: Spring Boot :: (v2.3.1.RELEASE)
2022-04-07 22:00:49.490 INFO 1 --- [ main] br.com.alura.forum.ForumApplication : Starting ForumApplication v0.0.1-SNAPSHOT on fcdc1a9a70c2 with PID 1 (/app.jar started by spring in /) 2022-04-07 22:00:49.506 INFO 1 --- [ main] br.com.alura.forum.ForumApplication : No active profile set, falling back to default profiles: default
Oi Victor,
O nome correto da variável deve ser: SPRING_PROFILES_ACTIVE
Com a nova versão do Spring Boot 3.0 estou tendo problemas com a classe TokenService, no método isTokenValid, parece que é algo relacionado com a dependencia do jwt, parece que a dependencia do jwt não tem mais suporte do jakarta com a nova versão do spring. Gostaria de saber oq fazer. Na classe Security, já troquei os antMatchers para requestMatchers mas n resolveu. Na IDE diz que está deprecated, gostaria de saber como corrigir.
Postei essa dúvida em outro lugar, acho q aqui era o local correto.
Oi Victor!
A recomendação é utilizar essa outra lib:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.2.1</version>
</dependency>
E atualizar o código da classe TokenService
:
@Service
public class TokenService {
@Value("${forum.jwt.secret}")
private String secret;
public String gerarToken(Authentication authentication) {
try {
Usuario logado = (Usuario) authentication.getPrincipal();
Algorithm algoritmo = Algorithm.HMAC256(secret);
return JWT.create()
.withIssuer("API do Fórum da Alura")
.withSubject(logado.getId().toString())
.withExpiresAt(dataExpiracao())
.sign(algoritmo);
} catch (JWTCreationException exception){
throw new RuntimeException("erro ao gerar token jwt", exception);
}
}
public String getSubject(String tokenJWT) {
try {
Algorithm algoritmo = Algorithm.HMAC256(secret);
return JWT.require(algoritmo)
.withIssuer("API do Fórum da Alura")
.build()
.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"));
}
}
Como ficaria o Filter então? Outra dúvida, Ao inves de utilizar esse método dataExpiração posso continuar usando como antes como variavel de ambiente?
Pode continuar sim usando o expiration. Nesse exemplo que mandei está com expiração de duas horas apenas.
O Filter:
public class AutenticacaoViaTokenFilter extends OncePerRequestFilter {
private TokenService tokenService;
private UsuarioRepository repository;
public AutenticacaoViaTokenFilter(TokenService tokenService, UsuarioRepository repository) {
this.tokenService = tokenService;
this.repository = repository;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String tokenJWT = recuperarToken(request);
if (tokenJWT != null) {
String subject = tokenService.getSubject(tokenJWT);
Usuario usuario = repository.getReferenceById(Long.parseLong(subject));
Authentication authentication = new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String recuperarToken(HttpServletRequest request) {
String authorizationHeader = request.getHeader("Authorization");
if (authorizationHeader != null) {
return authorizationHeader.replace("Bearer ", "");
}
return null;
}
}
Agora estou tendo outro problema com a nova versão do Spring.
Quando eu acesso um recurso(uri) que não existe e eu não estou logado, me retorna erro 401, ok,tudo certo! Mas quando eu estou logado e acesso um recurso que não existe, continua retornando 401 ao inves de 404. Não sei o motivo disso estar acontecendo, isso começou a acontecer quando eu mudei pra versão 3.0 do Spring Boot.
http.authorizeHttpRequests()
.requestMatchers("/auth","/users/register").permitAll()
.requestMatchers("/users" , "/users/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().cors()
.and().headers().frameOptions().disable()
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS
.and().addFilterBefore(new AuthenticationJWTFilter(tokenService, userRepository), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(new UnauthorizedHandler())
.and().exceptionHandling().accessDeniedHandler(new ForbiddenHandler());
Vai dependeder de como está sua classe RestControllerAdvice
em relação ao tratamento de exceptions.
A classe RestControllerAdvice minha não captura exception de not found , exemplo : localhost:8080/recursoinválido . Poderia me ajudar a como fazer o spring capturar isso então? Pq ele está retornando 401 e n 404. Mesmo quando eu estou logado,aparece 401 e meu security está anyRequests().authenticated();
Com a implementação que você fez no curso, isso funciona certinho. Quando acesso um recurso (uri) que não existe, e eu estou logado, retorna 404 , nessa nova implementação que você passou na resposta da nova dependencia do JWT, na classe TokenService e filter, isso agora começou a bugar na minha aplicação, mesmo quando estou logado em um recurso que não existe, volta o erro 401 ao inves do 404. O meu HandlerAdvice, eu não alterei nada mesmo, oq tá falhando é a nova implementação do token.
Tenho certeza, acabei de testar novamente, oq está bugando é a nova implementação que você passou na resposta. Não sei se é na classe filter ou tokenservice
Um handler para tratar os erros:
@RestControllerAdvice
public class TratadorDeErros {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity tratarErro404() {
return ResponseEntity.notFound().build();
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity tratarErro400(MethodArgumentNotValidException ex) {
var erros = ex.getFieldErrors();
return ResponseEntity.badRequest().body(erros.stream().map(DadosErroValidacao::new).toList());
}
@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity tratarErro400(DataIntegrityViolationException ex) {
return ResponseEntity.badRequest().body(ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity tratarErro500(Exception ex) {
return ResponseEntity.internalServerError().body(ex.getMessage());
}
private record DadosErroValidacao(String campo, String mensagem) {
public DadosErroValidacao(FieldError erro) {
this(erro.getField(), erro.getDefaultMessage());
}
}
}
Não funcionou, continua com o erro 401!
Quando estou sem nenhum usuário logado :
Ok, está funcionando corretamente, mas olha essa outra print :
Mesmo quando eu passo um token VÁLIDO, o erro continua sendo 401 ao inves de 404, isso começou a acontecer com a nova implementação que você passou, isso não tem relação com o handler, o problema está acontecendo antes de chegar no Handler, o problema é no filtro ou no tokenservice. Você poderia conferir isso em algum projeto seu pfv? com a implementação que você me passou.
O erro é muito esquisito pois o erro continua dando 401 mesmo com o usuário autenticado :
É possivel observar que o método recupera o usuário logado, tem um usuário logado ali, mas quando faz o filterChain.doFilter ele desloga automaticamente quando o recurso é inválido, muito esquisito.
Alguém sabe resolver?
Talvez seja por conta dessas classes que você configurou:
.exceptionHandling().authenticationEntryPoint(new UnauthorizedHandler())
.and().exceptionHandling().accessDeniedHandler(new ForbiddenHandler());
Posta aqui o código dessas duas classes
Pior que não, mesmo quando eu comento essas 2 linhas, continua com o problema.
Se puder dar uma olhada aqui : https://github.com/Almadavic/security-standard
Para o Spring devolver 404 quando chegar um requisição para uma url não mapeada você vai precisar sobrescrever essas propriedades:
spring.mvc.throw-exception-if-no-handler-found=true
spring.web.resources.add-mappings=false
E adicionar no @RestControllerAdvice
o tratamento para a exception NoHandlerFoundException
:
@ExceptionHandler({EntityNotFoundException.class, NoHandlerFoundException.class})
public ResponseEntity tratarErro404() {
return ResponseEntity.notFound().build();
}
Fiz, deu certinho. Você poderia me explicar por que isso está ocorrendo na nova versão? Sendo que antes não ocorria?
Foi uma mudança que fizeram no spring. Agora esse é o novo padrão.
Resolvido.