From dd45cb88bfe6fb9487653f5726beb78a4670c9df Mon Sep 17 00:00:00 2001 From: Samuel Modesto Date: Sun, 20 Jul 2025 13:43:07 -0300 Subject: [PATCH 1/3] =?UTF-8?q?criando=20a=20tokeniza=C3=A7=C3=A3o=20da=20?= =?UTF-8?q?api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cadastral/pom.xml | 13 +++++ .../crud/config/MongoScriptInitializer.java | 15 ++++-- .../security/JwtAuthenticationFilter.java | 52 +++++++++++++++++++ .../crud/config/security/JwtProvider.java | 51 ++++++++++++++++++ .../crud/config/security/SecurityConfig.java | 32 +++++------- .../crud/config/security/UserDatailsImpl.java | 2 +- .../security/UserDetailsServiceImpl.java | 6 +-- .../java/com/unipampa/crud/dto/JwtDTO.java | 15 ++++++ .../java/com/unipampa/crud/dto/LoginDTO.java | 13 +++++ .../java/com/unipampa/crud/entities/User.java | 2 + .../unipampa/crud/resources/AuthResource.java | 50 ++++++++++++++++++ 11 files changed, 225 insertions(+), 26 deletions(-) create mode 100644 cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java create mode 100644 cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java create mode 100644 cadastral/src/main/java/com/unipampa/crud/dto/JwtDTO.java create mode 100644 cadastral/src/main/java/com/unipampa/crud/dto/LoginDTO.java create mode 100644 cadastral/src/main/java/com/unipampa/crud/resources/AuthResource.java diff --git a/cadastral/pom.xml b/cadastral/pom.xml index c5468ce4..1f8661b4 100644 --- a/cadastral/pom.xml +++ b/cadastral/pom.xml @@ -107,6 +107,19 @@ spring-cloud-starter-config + + + io.jsonwebtoken + jjwt + 0.9.1 + + + + javax.xml.bind + jaxb-api + 2.3.1 + + diff --git a/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java b/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java index b5772e33..115b0209 100644 --- a/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java +++ b/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java @@ -6,6 +6,7 @@ import org.bson.Document; import org.springframework.boot.CommandLineRunner; import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import java.util.List; @@ -53,16 +54,22 @@ public void run(String... args) throws Exception { if (userCount == 0) { System.out.println("Usuários não encontrados, criando inicialização..."); + Document roleAdmin = roleCollection.find(new Document("roleName", "ROLE_ADMINISTRATOR")).first(); + + if (roleAdmin == null) { + throw new IllegalStateException("Role 'ROLE_ADMINISTRATOR' não encontrada no banco!"); + } + userCollection.insertMany(List.of( new Document("_id", "admin-1") .append("userName", dotenv.get("ADMIN1_USERNAME")) - .append("password", passwordEncoder.encode(dotenv.get("ADMIN1_PASSWORD"))) // Senha criptografada - .append("roles", UserType.ROLE_ADMINISTRATOR), + .append("password", passwordEncoder.encode(dotenv.get("ADMIN1_PASSWORD"))) + .append("roles", List.of(roleAdmin)), new Document("_id", "admin-2") .append("userName", dotenv.get("ADMIN2_USERNAME")) - .append("password", passwordEncoder.encode(dotenv.get("ADMIN2_PASSWORD"))) // Senha criptografada - .append("roles", UserType.ROLE_ADMINISTRATOR) + .append("password", passwordEncoder.encode(dotenv.get("ADMIN2_PASSWORD"))) + .append("roles", List.of(roleAdmin)) )); System.out.println("Usuários criados com sucesso!"); diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java new file mode 100644 index 00000000..72f5c03a --- /dev/null +++ b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java @@ -0,0 +1,52 @@ +package com.unipampa.crud.config.security; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + @Autowired + private JwtProvider jwtProvider; + + @Autowired + private UserDetailsServiceImpl userDetailsService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String token = extractToken(request); + if (token != null && jwtProvider.validateToken(token)) { + String username = jwtProvider.getUsernameFromToken(token); + logger.info("username extraído do token: " + username); + + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + logger.info("user details encontrado: " + userDetails); + + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + filterChain.doFilter(request, response); + + } + + private String extractToken(HttpServletRequest request) { + String authHeader = request.getHeader("Authorization"); + if (authHeader != null && authHeader.startsWith("Bearer ")) { + return authHeader.substring(7); + } + return null; + } +} diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java new file mode 100644 index 00000000..40af135e --- /dev/null +++ b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java @@ -0,0 +1,51 @@ +package com.unipampa.crud.config.security; + +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import java.util.Date; + +@Log4j2 +@Component +public class JwtProvider { + + @Value( "${ead.auth.jwtSecret}") + private String jwtSecret; + + @Value( "${ead.auth.jwtExpirationMs}") + private int jwtExpirationMs; + + public String generateJwtToken(Authentication authentication) { + UserDetails userPrincipal = (UserDatailsImpl) authentication.getPrincipal(); + return Jwts.builder() + .setSubject(userPrincipal.getUsername()) + .setIssuedAt(new Date()) + .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) + .signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, jwtSecret) + .compact(); + } + + public String getUsernameFromToken(String token) { + return Jwts.parser() + .setSigningKey(jwtSecret) + .parseClaimsJws(token) + .getBody() + .getSubject(); + } + + public boolean validateToken(String authToken) { + try { + Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken); + return true; + } catch (JwtException e) { + return false; + } + } + + +} diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java b/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java index 459c9cc1..885eee6f 100644 --- a/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java +++ b/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java @@ -13,6 +13,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableMethodSecurity @@ -21,34 +22,29 @@ public class SecurityConfig { private static final String HOST = "HOST"; private static final String ADMIN = "ADMINISTRATOR"; private static final String GUEST = "GUEST"; + @Autowired private UserDetailsServiceImpl userDetailsService; + @Autowired + private JwtAuthenticationFilter jwtAuthenticationFilter; + private static final String[] PUBLIC_MATCHERS = { - "/users/**" + "/users/**", + "/auth/**" }; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.authorizeHttpRequests(auth -> auth - .requestMatchers(HttpMethod.POST, PUBLIC_MATCHERS).permitAll() - .requestMatchers("/accommodations/images/**").permitAll() - .requestMatchers(HttpMethod.POST, "/accommodations/**").hasAnyRole(HOST, ADMIN) - .requestMatchers(HttpMethod.GET, "/accommodations/**").hasAnyRole(HOST, GUEST, ADMIN) - .requestMatchers(HttpMethod.GET, "/accommodations/{id}").hasAnyRole(HOST, GUEST, ADMIN) - .requestMatchers(HttpMethod.PUT, "/accommodations/{id}").hasAnyRole(HOST, ADMIN) - .requestMatchers(HttpMethod.DELETE, "/accommodations/{id}").hasAnyRole(HOST, ADMIN) - - .requestMatchers(HttpMethod.GET, "/users").hasRole(ADMIN) - .requestMatchers(HttpMethod.GET, "/users/email/**").hasAnyRole(ADMIN, HOST, GUEST) - .requestMatchers(HttpMethod.GET, "/users/{id}").hasAnyRole(ADMIN, HOST, GUEST) - .requestMatchers(HttpMethod.PUT, "/users/{id}").hasAnyRole(ADMIN, HOST, GUEST) - .requestMatchers(HttpMethod.DELETE, "/users/{id}").hasAnyRole(ADMIN, HOST, GUEST) - + http + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(auth -> auth + .requestMatchers(PUBLIC_MATCHERS).permitAll() .anyRequest().authenticated() ) - .httpBasic(Customizer.withDefaults()) - .csrf(AbstractHttpConfigurer::disable); + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + .httpBasic(Customizer.withDefaults()); + return http.build(); } diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/UserDatailsImpl.java b/cadastral/src/main/java/com/unipampa/crud/config/security/UserDatailsImpl.java index 72fdfb03..d071fae9 100644 --- a/cadastral/src/main/java/com/unipampa/crud/config/security/UserDatailsImpl.java +++ b/cadastral/src/main/java/com/unipampa/crud/config/security/UserDatailsImpl.java @@ -29,7 +29,7 @@ public class UserDatailsImpl implements UserDetails { public static UserDatailsImpl build(User user) { List authorities = user.getRoles().stream() - .map(role -> new SimpleGrantedAuthority(role.getAuthority())) + .map(role -> new SimpleGrantedAuthority(role.getRoleName().name())) .collect(Collectors.toList()); return new UserDatailsImpl ( diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java b/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java index 3182d833..d29b1d72 100644 --- a/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java +++ b/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java @@ -18,9 +18,9 @@ public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { try { - var user = userRepository.findByUserName(username) - .orElseThrow(() -> new UsernameNotFoundException(USER_NOT_FOUND + username)); - return UserDatailsImpl.build(user); + var user = userRepository.findByUserName(username); + var opt = user.orElseThrow(() -> new UsernameNotFoundException(USER_NOT_FOUND + username)); + return UserDatailsImpl.build(opt); } catch (Exception e) { throw new UsernameNotFoundException("Erro de conectividade ou ao carregar os dados do usuário: " + username, e); } diff --git a/cadastral/src/main/java/com/unipampa/crud/dto/JwtDTO.java b/cadastral/src/main/java/com/unipampa/crud/dto/JwtDTO.java new file mode 100644 index 00000000..aaac8b48 --- /dev/null +++ b/cadastral/src/main/java/com/unipampa/crud/dto/JwtDTO.java @@ -0,0 +1,15 @@ +package com.unipampa.crud.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@Data +@RequiredArgsConstructor +public class JwtDTO { + + @NotNull + private final String token; + + private final String type = "Bearer"; +} diff --git a/cadastral/src/main/java/com/unipampa/crud/dto/LoginDTO.java b/cadastral/src/main/java/com/unipampa/crud/dto/LoginDTO.java new file mode 100644 index 00000000..413497c4 --- /dev/null +++ b/cadastral/src/main/java/com/unipampa/crud/dto/LoginDTO.java @@ -0,0 +1,13 @@ +package com.unipampa.crud.dto; + +import jakarta.validation.constraints.NotBlank; + +public record LoginDTO( + + @NotBlank + String username, + + @NotBlank + String password + +) {} diff --git a/cadastral/src/main/java/com/unipampa/crud/entities/User.java b/cadastral/src/main/java/com/unipampa/crud/entities/User.java index c78de22e..bc351988 100644 --- a/cadastral/src/main/java/com/unipampa/crud/entities/User.java +++ b/cadastral/src/main/java/com/unipampa/crud/entities/User.java @@ -27,6 +27,7 @@ public class User implements Serializable { @Id private String id; + @Field("userName") private String userName; private String password; private String cpf; @@ -46,4 +47,5 @@ public class User implements Serializable { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'") private LocalDateTime lastUpdateDate; + } diff --git a/cadastral/src/main/java/com/unipampa/crud/resources/AuthResource.java b/cadastral/src/main/java/com/unipampa/crud/resources/AuthResource.java new file mode 100644 index 00000000..59e472de --- /dev/null +++ b/cadastral/src/main/java/com/unipampa/crud/resources/AuthResource.java @@ -0,0 +1,50 @@ +package com.unipampa.crud.resources; + +import com.unipampa.crud.config.security.JwtProvider; +import com.unipampa.crud.dto.JwtDTO; +import com.unipampa.crud.dto.LoginDTO; +import jakarta.validation.Valid; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Log4j2 +@RestController +@RequestMapping("/auth") +public class AuthResource { + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private JwtProvider jwtProvider; + + + + @PostMapping() + public ResponseEntity authenticateUser(@Valid @RequestBody LoginDTO loginDTO) { + try { + Authentication authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(loginDTO.username(), loginDTO.password())); + + SecurityContextHolder.getContext().setAuthentication(authentication); + + String jwt = jwtProvider.generateJwtToken(authentication); + return ResponseEntity.ok(new JwtDTO(jwt)); + + } catch (Exception e) { + System.out.println("🚨 Falha na autenticação: " + e.getClass().getSimpleName() + " - " + e.getMessage()); + return ResponseEntity.status(401).body(null); + } + + } + +} From 83d3d6a951a3217e421f1819217309b0ba94c311 Mon Sep 17 00:00:00 2001 From: Samuel Modesto Date: Thu, 24 Jul 2025 15:02:16 -0300 Subject: [PATCH 2/3] =?UTF-8?q?corre=C3=A7=C3=A3o=20da=20tokeniza=C3=A7?= =?UTF-8?q?=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cadastral/pom.xml | 11 +++ .../crud/config/MongoScriptInitializer.java | 10 +++ .../unipampa/crud/config/RabbitConfig.java | 24 ++++++ .../crud/config/security/JwtProvider.java | 19 +++-- .../crud/config/security/SecurityConfig.java | 14 +--- .../crud/dto/AccommodationMessageDTO.java | 11 +++ .../com/unipampa/crud/mappers/UserMapper.java | 14 ++++ .../crud/resources/AccommodationResource.java | 81 ++++++++++--------- .../unipampa/crud/resources/UserResource.java | 14 +++- .../crud/sender/AccommodationSender.java | 4 + .../src/main/resources/application-dev.yml | 2 +- .../example/rent/dto/AccommodationDto.java | 2 +- .../rent/receiver/AccommodationReceiver.java | 1 + 13 files changed, 148 insertions(+), 59 deletions(-) create mode 100644 cadastral/src/main/java/com/unipampa/crud/config/RabbitConfig.java create mode 100644 cadastral/src/main/java/com/unipampa/crud/dto/AccommodationMessageDTO.java create mode 100644 cadastral/src/main/java/com/unipampa/crud/mappers/UserMapper.java diff --git a/cadastral/pom.xml b/cadastral/pom.xml index 1f8661b4..b31f3daf 100644 --- a/cadastral/pom.xml +++ b/cadastral/pom.xml @@ -120,6 +120,12 @@ 2.3.1 + + org.mapstruct + mapstruct + 1.5.5.Final + + @@ -146,6 +152,11 @@ lombok ${lombok.version} + + org.mapstruct + mapstruct-processor + 1.5.5.Final + diff --git a/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java b/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java index 115b0209..1523c904 100644 --- a/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java +++ b/cadastral/src/main/java/com/unipampa/crud/config/MongoScriptInitializer.java @@ -69,6 +69,16 @@ public void run(String... args) throws Exception { new Document("_id", "admin-2") .append("userName", dotenv.get("ADMIN2_USERNAME")) .append("password", passwordEncoder.encode(dotenv.get("ADMIN2_PASSWORD"))) + .append("roles", List.of(roleAdmin)), + + new Document("_id", "admin-3") + .append("userName", dotenv.get("ADMIN3_USERNAME")) + .append("password", passwordEncoder.encode(dotenv.get("ADMIN3_PASSWORD"))) + .append("roles", List.of(roleAdmin)), + + new Document("_id", "admin-4") + .append("userName", dotenv.get("ADMIN4_USERNAME")) + .append("password", passwordEncoder.encode(dotenv.get("ADMIN4_PASSWORD"))) .append("roles", List.of(roleAdmin)) )); diff --git a/cadastral/src/main/java/com/unipampa/crud/config/RabbitConfig.java b/cadastral/src/main/java/com/unipampa/crud/config/RabbitConfig.java new file mode 100644 index 00000000..d9364a3c --- /dev/null +++ b/cadastral/src/main/java/com/unipampa/crud/config/RabbitConfig.java @@ -0,0 +1,24 @@ +package com.unipampa.crud.config; + +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RabbitConfig { + + @Bean + public Jackson2JsonMessageConverter jackson2JsonMessageConverter() { + return new Jackson2JsonMessageConverter(); + } + + @Bean + public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, + Jackson2JsonMessageConverter converter) { + RabbitTemplate template = new RabbitTemplate(connectionFactory); + template.setMessageConverter(converter); + return template; + } +} diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java index 40af135e..0b57e785 100644 --- a/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java +++ b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java @@ -1,7 +1,6 @@ package com.unipampa.crud.config.security; -import io.jsonwebtoken.JwtException; -import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.*; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; @@ -42,10 +41,18 @@ public boolean validateToken(String authToken) { try { Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken); return true; - } catch (JwtException e) { - return false; + + } catch (SignatureException e) { + log.error("Invalid JWT signature: {}", e.getMessage()); + } catch (MalformedJwtException e) { + log.error("Invalid JWT token: {}", e.getMessage()); + } catch (ExpiredJwtException e) { + log.error("JWT token is expired: {}", e.getMessage()); + } catch (UnsupportedJwtException e) { + log.error("JWT token is unsupported: {}", e.getMessage()); + } catch (IllegalArgumentException e) { + log.error("JWT claims string is empty: {}", e.getMessage()); } + return false; } - - } diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java b/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java index 885eee6f..13fd1628 100644 --- a/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java +++ b/cadastral/src/main/java/com/unipampa/crud/config/security/SecurityConfig.java @@ -1,9 +1,9 @@ package com.unipampa.crud.config.security; +import jakarta.ws.rs.HttpMethod; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -19,27 +19,19 @@ @EnableMethodSecurity public class SecurityConfig { - private static final String HOST = "HOST"; - private static final String ADMIN = "ADMINISTRATOR"; - private static final String GUEST = "GUEST"; - @Autowired private UserDetailsServiceImpl userDetailsService; @Autowired private JwtAuthenticationFilter jwtAuthenticationFilter; - private static final String[] PUBLIC_MATCHERS = { - "/users/**", - "/auth/**" - }; - @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth - .requestMatchers(PUBLIC_MATCHERS).permitAll() + .requestMatchers(HttpMethod.POST, "/users/**", "/auth/**").permitAll() + .requestMatchers(HttpMethod.GET, "/accommodations", "/accommodations/**", "/images/**").permitAll() .anyRequest().authenticated() ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) diff --git a/cadastral/src/main/java/com/unipampa/crud/dto/AccommodationMessageDTO.java b/cadastral/src/main/java/com/unipampa/crud/dto/AccommodationMessageDTO.java new file mode 100644 index 00000000..fb20fc99 --- /dev/null +++ b/cadastral/src/main/java/com/unipampa/crud/dto/AccommodationMessageDTO.java @@ -0,0 +1,11 @@ +package com.unipampa.crud.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class AccommodationMessageDTO { + private BigDecimal price; + private int maxOccupancy; +} diff --git a/cadastral/src/main/java/com/unipampa/crud/mappers/UserMapper.java b/cadastral/src/main/java/com/unipampa/crud/mappers/UserMapper.java new file mode 100644 index 00000000..7a7db77e --- /dev/null +++ b/cadastral/src/main/java/com/unipampa/crud/mappers/UserMapper.java @@ -0,0 +1,14 @@ +package com.unipampa.crud.mappers; + +import com.unipampa.crud.dto.UserDTO; +import com.unipampa.crud.entities.User; +import org.mapstruct.*; + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface UserMapper { + + + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + void updateUserFromDto(UserDTO dto, @MappingTarget User entity); + +} diff --git a/cadastral/src/main/java/com/unipampa/crud/resources/AccommodationResource.java b/cadastral/src/main/java/com/unipampa/crud/resources/AccommodationResource.java index 71f3f9e7..17f94c8a 100644 --- a/cadastral/src/main/java/com/unipampa/crud/resources/AccommodationResource.java +++ b/cadastral/src/main/java/com/unipampa/crud/resources/AccommodationResource.java @@ -24,6 +24,7 @@ import org.springframework.http.MediaType; import org.springframework.http.MediaTypeFactory; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -58,6 +59,8 @@ public class AccommodationResource { @Autowired private List validations; + + @PreAuthorize( "hasAnyRole('ROLE_ADMINISTRATOR', 'ROLE_HOST')" ) @PostMapping @Operation(summary = "Criar uma nova acomodação", description = "Valida e salva uma nova acomodação no sistema.") @@ -88,43 +91,7 @@ public ResponseEntity save( return ResponseEntity.created(location).body(accomodationResponseDTO); } - @GetMapping - @Operation(summary = "Listar todas as acomodações", - description = "Retorna uma lista contendo todas as acomodações cadastradas.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Lista retornada com sucesso", - content = @Content(schema = @Schema(implementation = AccommodationDTO.class))) - }) - public ResponseEntity> findAll() { - List accommodationDtos = accommodationService.findAll() - .stream() - .map(accommodation -> accommodationMapper.toDTO(accommodation)) - .collect(Collectors.toList()); - - return ResponseEntity.ok(accommodationDtos); - } - - @GetMapping("{id}") - @Operation(summary = "Buscar acomodação por ID", - description = "Retorna os dados de uma acomodação específica a partir do seu ID.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Acomodação encontrada", - content = @Content(schema = @Schema(implementation = AccommodationDTO.class))), - @ApiResponse(responseCode = "404", description = "Acomodação não encontrada", - content = @Content(schema = @Schema(implementation = ErrorResponse.class))) - }) - public ResponseEntity getById(@PathVariable("id") String id) { - Optional accommodationOptional = accommodationService.findById(id); - - if (accommodationOptional.isPresent()) { - AccommodationDTO dto = accommodationMapper.toDTO(accommodationOptional.get()); - return ResponseEntity.ok(dto); - } - - ErrorResponse errorResponse = new ErrorResponse("Acomodação não encontrada para o ID fornecido.", LocalDateTime.now()); - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); - } - + @PreAuthorize( "hasAnyRole('ROLE_ADMINISTRATOR', 'ROLE_HOST')" ) @DeleteMapping("{id}") @Operation(summary = "Deletar acomodação por ID", description = "Remove uma acomodação existente a partir do seu ID.") @@ -143,6 +110,7 @@ public ResponseEntity deleteAccommodation(@PathVariable("id") String id) return ResponseEntity.noContent().build(); } + @PreAuthorize( "hasAnyRole('ROLE_ADMINISTRATOR', 'ROLE_HOST')" ) @PutMapping("{id}") @Operation(summary = "Atualizar acomodação por ID", description = "Atualiza os dados de uma acomodação existente.") @@ -188,6 +156,44 @@ public ResponseEntity updateAccommodation( } } + @GetMapping + @Operation(summary = "Listar todas as acomodações", + description = "Retorna uma lista contendo todas as acomodações cadastradas.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Lista retornada com sucesso", + content = @Content(schema = @Schema(implementation = AccommodationDTO.class))) + }) + public ResponseEntity> findAll() { + List accommodationDtos = accommodationService.findAll() + .stream() + .map(accommodation -> accommodationMapper.toDTO(accommodation)) + .collect(Collectors.toList()); + + return ResponseEntity.ok(accommodationDtos); + } + + @GetMapping("{id}") + @Operation(summary = "Buscar acomodação por ID", + description = "Retorna os dados de uma acomodação específica a partir do seu ID.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Acomodação encontrada", + content = @Content(schema = @Schema(implementation = AccommodationDTO.class))), + @ApiResponse(responseCode = "404", description = "Acomodação não encontrada", + content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + public ResponseEntity getById(@PathVariable("id") String id) { + Optional accommodationOptional = accommodationService.findById(id); + + if (accommodationOptional.isPresent()) { + AccommodationDTO dto = accommodationMapper.toDTO(accommodationOptional.get()); + return ResponseEntity.ok(dto); + } + + ErrorResponse errorResponse = new ErrorResponse("Acomodação não encontrada para o ID fornecido.", LocalDateTime.now()); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); + } + + @GetMapping("images/{id}/{filename}") public ResponseEntity getImage(@PathVariable String id, @PathVariable String filename) throws MalformedURLException, MalformedURLException { Path path = Paths.get(uploadsDir + id).resolve(filename); @@ -197,7 +203,6 @@ public ResponseEntity getImage(@PathVariable String id, @PathVariable } Resource resource = new UrlResource(path.toUri()); - MediaType contentType = MediaTypeFactory.getMediaType(resource) .orElse(MediaType.APPLICATION_OCTET_STREAM); diff --git a/cadastral/src/main/java/com/unipampa/crud/resources/UserResource.java b/cadastral/src/main/java/com/unipampa/crud/resources/UserResource.java index 656c3113..b92de73c 100644 --- a/cadastral/src/main/java/com/unipampa/crud/resources/UserResource.java +++ b/cadastral/src/main/java/com/unipampa/crud/resources/UserResource.java @@ -5,6 +5,7 @@ import com.unipampa.crud.dto.UserDTO; import com.unipampa.crud.entities.Role; import com.unipampa.crud.entities.User; +import com.unipampa.crud.mappers.UserMapper; import com.unipampa.crud.service.RoleService; import com.unipampa.crud.service.UserService; import com.unipampa.crud.validations.ValidationsSignup; @@ -18,6 +19,7 @@ import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; @@ -35,6 +37,7 @@ public class UserResource { private static final String USER_NOT_FOUND = "Usuário não encontrado para esse email!"; private static final String USER_SAVED_SUCCESSFULLY_LOG = "Usuário salvo com sucesso, username: {}"; private static final String ACTION_NOT_AUTHORIZED = "Você não pode executar essa ação"; + @Autowired private UserService userService; @@ -50,6 +53,9 @@ public class UserResource { @Autowired PasswordEncoder passwordEncoder; + @Autowired + private UserMapper userMapper; + public UserResource(UserService userService) { this.userService = userService; } @@ -73,6 +79,7 @@ public ResponseEntity saveUser(@RequestBody UserDTO userDto) { } + @PreAuthorize( "hasRole('ROLE_ADMINISTRATOR')" ) @GetMapping @Operation(summary = "Retorna todos os usuários cadastrados") public ResponseEntity> getAllUsers( @@ -84,6 +91,7 @@ public ResponseEntity> getAllUsers( return ResponseEntity.status(HttpStatus.OK).body(users); } + @PreAuthorize( "hasAnyRole('ROLE_ADMINISTRATOR', 'ROLE_HOST', 'ROLE_GUEST')" ) @GetMapping("email/{email}") @Operation(summary = "Retorna um usuario pelo email") public ResponseEntity getUserByEmail(@PathVariable("email") String email) { @@ -95,6 +103,7 @@ public ResponseEntity getUserByEmail(@PathVariable("email") String email return new ResponseEntity<>(user, HttpStatus.OK); } + @PreAuthorize( "hasAnyRole('ROLE_ADMINISTRATOR', 'ROLE_HOST', 'ROLE_GUEST')" ) @GetMapping("{id}") @Operation(summary = "Retorna um usuario pelo id") public ResponseEntity getUserById(@PathVariable("id") String id) { @@ -106,7 +115,7 @@ public ResponseEntity getUserById(@PathVariable("id") String id) { return new ResponseEntity<>(user, HttpStatus.OK); } - + @PreAuthorize( "hasAnyRole('ROLE_ADMINISTRATOR', 'ROLE_HOST', 'ROLE_GUEST')" ) @PutMapping("{id}") @Operation(summary = "Atualiza um usuario pelo id") public ResponseEntity updateUser(@RequestBody UserDTO userDTO, @PathVariable("id")String id) { @@ -116,11 +125,12 @@ public ResponseEntity updateUser(@RequestBody UserDTO userDTO, @PathVar } userService.isOwnerOrAdmin(user); var userModel = user.get(); - BeanUtils.copyProperties(userDTO, userModel); + userMapper.updateUserFromDto(userDTO, userModel); userService.save(userModel); return new ResponseEntity<>(userModel, HttpStatus.OK); } + @PreAuthorize( "hasAnyRole('ROLE_ADMINISTRATOR', 'ROLE_HOST', 'ROLE_GUEST')" ) @DeleteMapping("{id}") @Operation(summary = "Remove um usuario pelo seu id") public ResponseEntity deleteUser(@PathVariable("id") String id) { diff --git a/cadastral/src/main/java/com/unipampa/crud/sender/AccommodationSender.java b/cadastral/src/main/java/com/unipampa/crud/sender/AccommodationSender.java index 02537948..0659d3bc 100644 --- a/cadastral/src/main/java/com/unipampa/crud/sender/AccommodationSender.java +++ b/cadastral/src/main/java/com/unipampa/crud/sender/AccommodationSender.java @@ -1,6 +1,8 @@ package com.unipampa.crud.sender; +import com.unipampa.crud.dto.AccommodationMessageDTO; import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -21,6 +23,8 @@ public AccommodationSender(RabbitTemplate rabbitTemplate) { } public void sendMessage(Accommodation accommodation) { + AccommodationMessageDTO dto = new AccommodationMessageDTO(); + BeanUtils.copyProperties(accommodation, dto); rabbitTemplate.convertAndSend(exchange, "", accommodation); } diff --git a/cadastral/src/main/resources/application-dev.yml b/cadastral/src/main/resources/application-dev.yml index b6a856d8..6fb2e12f 100644 --- a/cadastral/src/main/resources/application-dev.yml +++ b/cadastral/src/main/resources/application-dev.yml @@ -55,4 +55,4 @@ eureka: service-url: defaultZone: 'http://${ead.serviceRegistry.username}:${ead.serviceRegistry.password}@localhost:8761/eureka' app: - uploads-dir: C:/Users/Samuel Modesto/OneDrive/Documentos/Projetos/TCC/imovato/backend/cadastral/uploads/imoveis \ No newline at end of file + uploads-dir: C:/Users/Samuel Modesto/OneDrive/Documentos/Projetos/TCC/imovato/backend/cadastral/uploads/ \ No newline at end of file diff --git a/transacional/src/main/java/com/example/rent/dto/AccommodationDto.java b/transacional/src/main/java/com/example/rent/dto/AccommodationDto.java index 35859b37..b4d6899b 100644 --- a/transacional/src/main/java/com/example/rent/dto/AccommodationDto.java +++ b/transacional/src/main/java/com/example/rent/dto/AccommodationDto.java @@ -3,6 +3,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) -public record AccommodationDto(Double price) { +public record AccommodationDto(Double price, int maxOccupancy) { } diff --git a/transacional/src/main/java/com/example/rent/receiver/AccommodationReceiver.java b/transacional/src/main/java/com/example/rent/receiver/AccommodationReceiver.java index 44a57698..634075f5 100644 --- a/transacional/src/main/java/com/example/rent/receiver/AccommodationReceiver.java +++ b/transacional/src/main/java/com/example/rent/receiver/AccommodationReceiver.java @@ -26,6 +26,7 @@ public void receive(@Payload AccommodationDto dto) { Accommodation accommodation = new Accommodation(); accommodation.setPrice(dto.price()); accommodation.setStatus(StatusAccommodation.AVAILABLE); + accommodation.setGuestCapacity(dto.maxOccupancy()); accommodationRepository.save(accommodation); } } From 1643de856fc93377a96e82267ac2acc63e6eae71 Mon Sep 17 00:00:00 2001 From: Samuel Modesto Date: Thu, 24 Jul 2025 16:52:22 -0300 Subject: [PATCH 3/3] =?UTF-8?q?implementa=C3=A7=C3=A3o=20para=20que=20o=20?= =?UTF-8?q?subject=20nao=20retorne=20o=20username=20mas=20sim=20o=20id=20q?= =?UTF-8?q?ue=20=C3=A9=20=C3=BAnico=20na=20arquitetura?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crud/config/security/JwtAuthenticationFilter.java | 6 +++--- .../com/unipampa/crud/config/security/JwtProvider.java | 7 +++---- .../crud/config/security/UserDetailsServiceImpl.java | 10 +++++++++- .../com/unipampa/crud/repository/UserRepository.java | 3 +++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java index 72f5c03a..f49cda92 100644 --- a/cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java +++ b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtAuthenticationFilter.java @@ -26,10 +26,10 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = extractToken(request); if (token != null && jwtProvider.validateToken(token)) { - String username = jwtProvider.getUsernameFromToken(token); - logger.info("username extraído do token: " + username); + String userId = jwtProvider.getSubjectFromToken(token); + logger.info("username extraído do token: " + userId); - UserDetails userDetails = userDetailsService.loadUserByUsername(username); + UserDetails userDetails = userDetailsService.loadUserByUserId(userId); logger.info("user details encontrado: " + userDetails); UsernamePasswordAuthenticationToken authentication = diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java index 0b57e785..6d2c80e2 100644 --- a/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java +++ b/cadastral/src/main/java/com/unipampa/crud/config/security/JwtProvider.java @@ -4,7 +4,6 @@ import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import java.util.Date; @@ -20,16 +19,16 @@ public class JwtProvider { private int jwtExpirationMs; public String generateJwtToken(Authentication authentication) { - UserDetails userPrincipal = (UserDatailsImpl) authentication.getPrincipal(); + UserDatailsImpl userPrincipal = (UserDatailsImpl) authentication.getPrincipal(); return Jwts.builder() - .setSubject(userPrincipal.getUsername()) + .setSubject(userPrincipal.getUserId()) .setIssuedAt(new Date()) .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) .signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, jwtSecret) .compact(); } - public String getUsernameFromToken(String token) { + public String getSubjectFromToken(String token) { return Jwts.parser() .setSigningKey(jwtSecret) .parseClaimsJws(token) diff --git a/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java b/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java index d29b1d72..f9f5985c 100644 --- a/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java +++ b/cadastral/src/main/java/com/unipampa/crud/config/security/UserDetailsServiceImpl.java @@ -2,6 +2,7 @@ import com.unipampa.crud.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -10,7 +11,7 @@ @Service public class UserDetailsServiceImpl implements UserDetailsService { - public static final String USER_NOT_FOUND = "Usuário não encontrado para o nome de usuário: "; + public static final String USER_NOT_FOUND = "Usuário não encontrado para o id de usuário: "; @Autowired private UserRepository userRepository; @@ -25,4 +26,11 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx throw new UsernameNotFoundException("Erro de conectividade ou ao carregar os dados do usuário: " + username, e); } } + + public UserDetails loadUserByUserId(String userId) throws AuthenticationCredentialsNotFoundException { + var user = userRepository.findById(userId); + var userModel = user.orElseThrow(() -> new AuthenticationCredentialsNotFoundException(USER_NOT_FOUND + userId)); + return UserDatailsImpl.build(userModel); + + } } diff --git a/cadastral/src/main/java/com/unipampa/crud/repository/UserRepository.java b/cadastral/src/main/java/com/unipampa/crud/repository/UserRepository.java index 62ac135b..3afdc61a 100644 --- a/cadastral/src/main/java/com/unipampa/crud/repository/UserRepository.java +++ b/cadastral/src/main/java/com/unipampa/crud/repository/UserRepository.java @@ -18,4 +18,7 @@ public interface UserRepository extends MongoRepository { @EntityGraph(attributePaths = {"roles"}, type = EntityGraph.EntityGraphType.FETCH) Optional findByUserName(String username); + + @EntityGraph(attributePaths = {"roles"}, type = EntityGraph.EntityGraphType.FETCH) + Optional findById(String userId); } \ No newline at end of file