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.
Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!
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=falseE 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.