diff --git a/src/main/java/pe/edu/utp/controller/ArticuloController.java b/src/main/java/pe/edu/utp/controller/ArticuloController.java index 3bc52ce..1b5fae7 100644 --- a/src/main/java/pe/edu/utp/controller/ArticuloController.java +++ b/src/main/java/pe/edu/utp/controller/ArticuloController.java @@ -2,13 +2,10 @@ import java.util.List; -import jakarta.validation.Valid; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -18,27 +15,23 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import pe.edu.utp.exception.NoDataFoundException; -import pe.edu.utp.converter.ArticuloConverter; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import pe.edu.utp.dto.ArticuloDto; import pe.edu.utp.entity.Articulo; +import pe.edu.utp.exception.NoDataFoundException; import pe.edu.utp.service.ArticuloService; @RestController @RequestMapping("/articulos") -@Validated +@RequiredArgsConstructor +@Slf4j public class ArticuloController { private final ArticuloService articuloService; - private final ArticuloConverter articuloConverter; - - @Autowired - public ArticuloController(ArticuloService articuloService, ArticuloConverter articuloConverter) { - this.articuloService = articuloService; - this.articuloConverter = articuloConverter; - } - @GetMapping public ResponseEntity> getAll( @@ -50,49 +43,33 @@ public ResponseEntity> getAll( @RequestParam(value = "limit", required = false, defaultValue = "5") int pageSize) { Pageable pageable = PageRequest.of(pageNumber, pageSize); - List articulos; + List articulos; if (marca == null || categoria == null || precioMin == null || precioMax == null) { articulos = articuloService.findAll(pageable); } else { - articulos = articuloService.findByCategoriaAndMarcaAndPrecio(categoria,marca,precioMin,precioMax, pageable); - - } - - if (articulos.isEmpty()) { - return ResponseEntity.noContent().build(); + articulos = articuloService.findByCategoriaAndMarcaAndPrecio(categoria, marca, precioMin, precioMax, pageable); } - List articulosDTO = articuloConverter.fromEntity(articulos); - return ResponseEntity.ok(articulosDTO); + return ResponseEntity.ok(articulos); } @GetMapping(value = "/{id}") - public ResponseEntity findById(@PathVariable("id") int id) - throws NoDataFoundException { - Articulo articulo = articuloService.findById(id); - if (articulo == null) { - return ResponseEntity.notFound().build(); - } - ArticuloDto articuloDTO = articuloConverter.fromEntity(articulo); - return ResponseEntity.ok(articuloDTO); + public ResponseEntity findById(@PathVariable("id") int id) { + log.info("Obteniendo articulo con ID: {}", id); + var response = articuloService.findById(id); + return ResponseEntity.ok(response); } @PostMapping - public ResponseEntity create(@Valid @RequestBody ArticuloDto articulo) { - Articulo registro = articuloService.save(articuloConverter.fromDto(articulo)); - ArticuloDto registroDTO = articuloConverter.fromEntity(registro); - return ResponseEntity.status(HttpStatus.CREATED).body(registroDTO); + public ResponseEntity create(@Valid @RequestBody ArticuloDto articuloDto) { + Articulo registro = articuloService.save(articuloDto); + return ResponseEntity.status(HttpStatus.CREATED).body(registro); } @PutMapping(value = "/{id}") - public ResponseEntity update( + public ResponseEntity update( @PathVariable("id") int id, @Valid @RequestBody ArticuloDto articuloDto) { - Articulo articuloUpdate = articuloService.update(articuloConverter.fromDto(articuloDto)); - - if (articuloUpdate == null) { - return ResponseEntity.notFound().build(); - } - ArticuloDto articuloUpdateDTO = articuloConverter.fromEntity(articuloUpdate); - return ResponseEntity.ok(articuloUpdateDTO); + Articulo articuloUpdate = articuloService.update(articuloDto, id); + return ResponseEntity.ok(articuloUpdate); } @DeleteMapping(value = "/{id}") diff --git a/src/main/java/pe/edu/utp/controller/UsuarioController.java b/src/main/java/pe/edu/utp/controller/UsuarioController.java index 37d431a..ed26285 100644 --- a/src/main/java/pe/edu/utp/controller/UsuarioController.java +++ b/src/main/java/pe/edu/utp/controller/UsuarioController.java @@ -1,13 +1,21 @@ package pe.edu.utp.controller; import java.util.List; -import lombok.RequiredArgsConstructor; -import org.apache.catalina.User; + import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import lombok.RequiredArgsConstructor; import pe.edu.utp.converter.UsuarioConverter; import pe.edu.utp.dto.LoginRequestDto; import pe.edu.utp.dto.LoginResponseDto; diff --git a/src/main/java/pe/edu/utp/dto/LoginResponseDto.java b/src/main/java/pe/edu/utp/dto/LoginResponseDto.java index 2862308..0c7ed2c 100644 --- a/src/main/java/pe/edu/utp/dto/LoginResponseDto.java +++ b/src/main/java/pe/edu/utp/dto/LoginResponseDto.java @@ -12,5 +12,4 @@ public class LoginResponseDto { private UsuarioResponseDto usuario; private String token; - private String refreshToken; } diff --git a/src/main/java/pe/edu/utp/security/ApplicationConfig.java b/src/main/java/pe/edu/utp/security/ApplicationConfig.java deleted file mode 100644 index 6fdc10f..0000000 --- a/src/main/java/pe/edu/utp/security/ApplicationConfig.java +++ /dev/null @@ -1,48 +0,0 @@ -package pe.edu.utp.security; - -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.dao.DaoAuthenticationProvider; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import pe.edu.utp.repository.UsuarioRepository; - -@Configuration -@RequiredArgsConstructor -public class ApplicationConfig { - - private final UsuarioRepository repository; - - @Bean - public UserDetailsService userDetailsService() { - return username -> - repository - .findByEmail(username) - .orElseThrow(() -> new UsernameNotFoundException("User not found")); - } - - @Bean - public AuthenticationProvider authenticationProvider() { - DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); - authProvider.setUserDetailsService(userDetailsService()); - authProvider.setPasswordEncoder(passwordEncoder()); - return authProvider; - } - - @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration config) - throws Exception { - return config.getAuthenticationManager(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B); - } -} diff --git a/src/main/java/pe/edu/utp/security/CustomerUserDetailsService.java b/src/main/java/pe/edu/utp/security/CustomerUserDetailsService.java new file mode 100644 index 0000000..83bf2d3 --- /dev/null +++ b/src/main/java/pe/edu/utp/security/CustomerUserDetailsService.java @@ -0,0 +1,30 @@ +package pe.edu.utp.security; + +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; +import pe.edu.utp.entity.Usuario; +import pe.edu.utp.repository.UsuarioRepository; + +@Service +@RequiredArgsConstructor +public class CustomerUserDetailsService implements UserDetailsService { + + private final UsuarioRepository usuarioRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // TODO Auto-generated method stub + Usuario usuario = usuarioRepository.findByEmail(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username)); + + return org.springframework.security.core.userdetails.User.builder() + .username(usuario.getEmail()) + .password(usuario.getPassword()) + .roles(usuario.getRol().name()).build(); + } + +} diff --git a/src/main/java/pe/edu/utp/security/JwtAuthenticationFilter.java b/src/main/java/pe/edu/utp/security/JwtAuthenticationFilter.java index 6fc644b..fae3b23 100644 --- a/src/main/java/pe/edu/utp/security/JwtAuthenticationFilter.java +++ b/src/main/java/pe/edu/utp/security/JwtAuthenticationFilter.java @@ -1,56 +1,102 @@ package pe.edu.utp.security; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + @Component @RequiredArgsConstructor @Slf4j public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtService jwtService; - private final UserDetailsService userDetailsService; + private final CustomerUserDetailsService customerUserDetailsService; + private final ObjectMapper objectMapper; @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - final String authHeader = request.getHeader("Authorization"); - log.info("authHeader valor es: " + authHeader); - final String jwt; - final String userEmail; - if (authHeader == null || !authHeader.startsWith("Bearer ")) { - log.info("No Authorization header or invalid format"); - filterChain.doFilter(request, response); - return; - } - jwt = authHeader.substring(7); - userEmail = jwtService.extractUsername(jwt); - log.info("User Email: {}", userEmail); - if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { - UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); - log.info("User Details: {}", userDetails); - if (jwtService.isTokenValid(jwt, userDetails)) { - UsernamePasswordAuthenticationToken authToken = - new UsernamePasswordAuthenticationToken( - userDetails, null, userDetails.getAuthorities()); - authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(authToken); - } else{ - log.info("Invalid token"); - } - } - filterChain.doFilter(request, response); + + + try{ + String jwt = jwtService.extractToken(request); + if(jwt == null ){ + log.info("JWT token is missing for request: {}", request.getRequestURI()); + filterChain.doFilter(request, response); // Pasa la solicitud si no hay JWT + return; // Importante: retornar después de pasar + } + + // Opcional: Si el usuario ya está autenticado, no procesar el JWT de nuevo + if (SecurityContextHolder.getContext().getAuthentication() != null && + SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) { + log.debug("User already authenticated in SecurityContext. Skipping JWT processing for {}.", request.getRequestURI()); + filterChain.doFilter(request, response); // Pasa la solicitud si ya está autenticado + return; + } + + Claims claims = jwtService.resolveClaims(request); // Esto puede lanzar ExpiredJwtException, etc. + + if(claims != null && jwtService.validateClaims(claims)){ + String username = jwtService.extractUsername(jwt); + UserDetails userDetails = customerUserDetailsService.loadUserByUsername(username); + + Authentication authentication = + new UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + log.debug("User '{}' authenticated via JWT with roles: {}. Proceeding with filter chain.", username, userDetails.getAuthorities()); + } else { + // Si el token no es válido (ej. claims null o no validan), pero no lanzó una excepción, + // loguear y opcionalmente denegar o simplemente pasar sin autenticar. + log.warn("Invalid JWT token or claims for {}. Not authenticating.", request.getRequestURI()); + // Podrías poner un error 401/403 aquí si un token _debe_ ser válido para cualquier ruta no permitAll + // response.setStatus(HttpStatus.UNAUTHORIZED.value()); + // response.setContentType(MediaType.APPLICATION_JSON_VALUE); + // objectMapper.writeValue(response.getWriter(), Map.of("error", "Invalid JWT Token")); + // return; + } + + // ¡ESTA ES LA LÍNEA CLAVE PARA LAS SOLICITUDES CON TOKEN VÁLIDO! + filterChain.doFilter(request, response); + + } catch (ExpiredJwtException ex) { + log.error("JWT token expired for request {}: {}", request.getRequestURI(), ex.getMessage()); + Map errorDetails = new HashMap<>(); + errorDetails.put("error", "JWT Token expired"); + response.setStatus(HttpStatus.UNAUTHORIZED.value()); // Un 401 es más apropiado para token expirado + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + objectMapper.writeValue(response.getWriter(), errorDetails); + + } catch (Exception e) { // Captura cualquier otra excepción relacionada con el JWT (firma inválida, etc.) + log.error("Error processing JWT token for request {}: {}", request.getRequestURI(), e.getMessage()); + Map errorDetails = new HashMap<>(); + errorDetails.put("error", "Invalid JWT token"); + response.setStatus(HttpStatus.FORBIDDEN.value()); // O 401 Unauthorized + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + objectMapper.writeValue(response.getWriter(), errorDetails); + + } + } } diff --git a/src/main/java/pe/edu/utp/security/JwtService.java b/src/main/java/pe/edu/utp/security/JwtService.java index a682373..82b3035 100644 --- a/src/main/java/pe/edu/utp/security/JwtService.java +++ b/src/main/java/pe/edu/utp/security/JwtService.java @@ -1,17 +1,21 @@ package pe.edu.utp.security; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.function.Function; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; + import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + @Service @RequiredArgsConstructor @Slf4j @@ -19,7 +23,8 @@ public class JwtService { private final PemReader pemReader; private final long accessTokenExpirationTime = 1000L * 60 * 24; - private final long refreshTokenExpirationTime = 1000L * 60 * 60 * 24; + private static final String TOKEN_HEADER = "Authorization"; + private static final String TOKEN_PREFIX = "Bearer "; public String extractUsername(String token) { return extractClaim(token, claims -> claims.get("username", String.class)); @@ -44,6 +49,30 @@ public String generateToken(UserDetails userDetails) { return generateToken(new HashMap<>(), userDetails); } + public String extractToken(HttpServletRequest request) { + String bearerToken = request.getHeader(TOKEN_HEADER); + if (bearerToken != null && bearerToken.startsWith(TOKEN_PREFIX)) { + return bearerToken.substring(TOKEN_PREFIX.length()); + } + return null; + } + + public Claims resolveClaims(HttpServletRequest request) { + try { + String jwt = extractToken(request); + if (jwt != null) { + return extractAllClaims(jwt); + } + return null; + } catch (ExpiredJwtException ex) { + request.setAttribute("expired", ex.getMessage()); + throw ex; + } catch (Exception ex) { + request.setAttribute("invalid", ex.getMessage()); + throw ex; + } + } + public String generateToken(Map extraClaims, UserDetails userDetails) { Claims claims = Jwts.claims(); claims.put("username", userDetails.getUsername()); @@ -59,24 +88,15 @@ public String generateToken(Map extraClaims, UserDetails userDet .compact(); } - public String generateRefreshToken(UserDetails userDetails) { - Claims claims = Jwts.claims(); - claims.put("username", userDetails.getUsername()); - - return Jwts.builder() - .setHeaderParam("typ", "JWT") - .setClaims(claims) - .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + refreshTokenExpirationTime)) - .signWith(pemReader.getPrivateKey(), SignatureAlgorithm.RS512) - .compact(); - } - public boolean isTokenValid(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername())) && !isTokenExpired(token); } + public boolean validateClaims(Claims claims) { + return claims.getExpiration().after(new Date()); + } + private boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } diff --git a/src/main/java/pe/edu/utp/security/SecurityConfiguration.java b/src/main/java/pe/edu/utp/security/SecurityConfiguration.java index bfef2d8..c12d635 100644 --- a/src/main/java/pe/edu/utp/security/SecurityConfiguration.java +++ b/src/main/java/pe/edu/utp/security/SecurityConfiguration.java @@ -1,24 +1,22 @@ package pe.edu.utp.security; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.config.Customizer; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; +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; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Configuration @EnableWebSecurity @@ -27,61 +25,54 @@ @Slf4j public class SecurityConfiguration { - private final JwtAuthenticationFilter jwtAuthenticationFilter; - private final AuthenticationProvider authenticationProvider; - - @Value("${spring.security.disabled:false}") - private boolean disabledSecurity; + private final JwtAuthenticationFilter jwtAuthenticationFilter; - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.csrf(AbstractHttpConfigurer::disable) - .cors(Customizer.withDefaults()); + @Value("${spring.security.disabled:false}") + private boolean disabledSecurity; - http.sessionManagement( - session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + /* + * Sprign security detecta un bean UserDetailsService y un PasswordEncoder, + * y automaticamente va a configurar una instancia de DaoAuthenticationProvider + * interno + * que utiliza el UserDetailsService y el PasswordEncoder. + * Luego crea una instancia de un ProviderManager, q registra al + * DaoAuthenticationProvider + * y lo devuelve como AuthenticationManager. + */ + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) + throws Exception { + return authenticationConfiguration.getAuthenticationManager(); + } - if (disabledSecurity) { - log.warn("SYSTEM: HTTP Security rules are disabled"); - http.authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()); - return http.build(); - } + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.authorizeHttpRequests( - authorize -> - authorize - .requestMatchers( - // **Aquí está el cambio para resolver el error de compilación** - AntPathRequestMatcher.antMatcher("/ws/**"), - AntPathRequestMatcher.antMatcher("/actuator/health"), - AntPathRequestMatcher.antMatcher("/usuarios-register"), - AntPathRequestMatcher.antMatcher("/usuarios/login"), - AntPathRequestMatcher.antMatcher("/error"), - // Las siguientes líneas también necesitan el AntPathRequestMatcher - AntPathRequestMatcher.antMatcher("/signup/**"), - AntPathRequestMatcher.antMatcher("/institutions"), - AntPathRequestMatcher.antMatcher("/v3/**"), - AntPathRequestMatcher.antMatcher("/doc/swagger-ui/**") - ) - .permitAll() - .anyRequest() - .authenticated()); + http.cors(cors -> cors.configure(http)); + http.csrf(AbstractHttpConfigurer::disable); + http.authorizeHttpRequests( + authorize -> authorize + .requestMatchers( + "/actuator/health", + "/usuarios-register", + "/usuarios/login", + "/signup/**", + "/v3/**", + "/doc/swagger-ui/**") + .permitAll() + .anyRequest() + .authenticated()); - http.authenticationProvider(authenticationProvider) - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - return http.build(); - } + http.sessionManagement( + sessionManagement -> sessionManagement + .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(List.of("http://127.0.0.1:5500", "http://localhost:5500")); - configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); - configuration.setAllowedHeaders(List.of("*")); - configuration.setAllowCredentials(true); // importante para JWT en WebSocket + http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + return http.build(); + } - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - return source; - } + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B); + } } diff --git a/src/main/java/pe/edu/utp/service/ArticuloService.java b/src/main/java/pe/edu/utp/service/ArticuloService.java index 1e1f171..b452a02 100644 --- a/src/main/java/pe/edu/utp/service/ArticuloService.java +++ b/src/main/java/pe/edu/utp/service/ArticuloService.java @@ -5,16 +5,17 @@ import org.springframework.data.domain.Pageable; import pe.edu.utp.exception.NoDataFoundException; +import pe.edu.utp.dto.ArticuloDto; import pe.edu.utp.entity.Articulo; public interface ArticuloService { - List findAll(Pageable page); + List findAll(Pageable page); - List findByCategoriaAndMarcaAndPrecio(String categoria, String marca,Double precioMin, Double precioMax, Pageable pageable); - Articulo findById(int id)throws NoDataFoundException; - Articulo save(Articulo articulo); - Articulo update(Articulo articulo); + List findByCategoriaAndMarcaAndPrecio(String categoria, String marca,Double precioMin, Double precioMax, Pageable pageable); + ArticuloDto findById(int id); + Articulo save(ArticuloDto articulo); + Articulo update(ArticuloDto articulo, int id); void delete(int id)throws NoDataFoundException; } diff --git a/src/main/java/pe/edu/utp/service/ArticuloServiceImpl.java b/src/main/java/pe/edu/utp/service/ArticuloServiceImpl.java index 5400903..dfbbec6 100644 --- a/src/main/java/pe/edu/utp/service/ArticuloServiceImpl.java +++ b/src/main/java/pe/edu/utp/service/ArticuloServiceImpl.java @@ -1,16 +1,17 @@ package pe.edu.utp.service; import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.Cacheable; + import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import pe.edu.utp.converter.ArticuloConverter; +import pe.edu.utp.dto.ArticuloDto; import pe.edu.utp.entity.Articulo; -import pe.edu.utp.exception.GeneralServiceException; import pe.edu.utp.exception.NoDataFoundException; -import pe.edu.utp.exception.ValidateServiceException; import pe.edu.utp.repository.ArticuloRepository; @Service @@ -19,68 +20,61 @@ public class ArticuloServiceImpl implements ArticuloService { private final ArticuloRepository articuloRepository; + private final ArticuloConverter articuloConverter; - @Cacheable(value = "CacheDemo", key = "#page") @Override - @Transactional(readOnly = true) - public List findAll(Pageable page) { - try { - return articuloRepository.findAll(page).toList(); - }catch (ValidateServiceException| NoDataFoundException e){ - log.info(e.getMessage()); - throw e; - }catch(Exception e){ - log.error(e.getMessage()); - throw new GeneralServiceException(e.getMessage()); - } + public List findAll(Pageable page) { + var articulos = articuloRepository.findAll(page).toList(); + return articuloConverter.fromEntity(articulos); } - - @Override - public List findByCategoriaAndMarcaAndPrecio(String categoria, String marca,Double precioMin, Double precioMax, Pageable pageable) { - return articuloRepository.findByCategoriaAndMarcaAndPrecioBetween(categoria, marca,precioMin,precioMax,pageable); + @Transactional(readOnly = true) + public List findByCategoriaAndMarcaAndPrecio(String categoria, String marca, Double precioMin, + Double precioMax, Pageable pageable) { + var result = articuloConverter.fromEntity( + articuloRepository.findByCategoriaAndMarcaAndPrecioBetween(categoria, marca, precioMin, precioMax, + pageable)); + return result; } @Override @Transactional(readOnly = true) - public Articulo findById(int id) throws NoDataFoundException { - Articulo articuloDB = articuloRepository.findById(id).orElse(null); - if (articuloDB != null) { - return articuloDB; - } else { - throw new NoDataFoundException("No existe un articulo con ese id"); - } - + public ArticuloDto findById(int id) { + Articulo articuloDB = findByArticleId(id); + return articuloConverter.fromEntity(articuloDB); } @Override @Transactional - public Articulo save(Articulo articulo) { - - return articuloRepository.save(articulo); + public Articulo save(ArticuloDto articulo) { + var entity = articuloConverter.fromDto(articulo); + return articuloRepository.save(entity); } @Override @Transactional - public Articulo update(Articulo articulo) { - Articulo registro = articuloRepository.findById(articulo.getId()).orElseThrow(); - registro.setNombre(articulo.getNombre()); - registro.setPrecio(articulo.getPrecio()); + public Articulo update(ArticuloDto articuloDto, int id) { + Articulo registro = findByArticleId(id); + registro.setNombre(articuloDto.getNombre()); + registro.setPrecio(articuloDto.getPrecio()); return articuloRepository.save(registro); } @Override @Transactional - public void delete(int id) throws NoDataFoundException { + public void delete(int id) { Articulo registro = articuloRepository.findById(id).orElse(null); - if (registro != null) { + if (registro != null) articuloRepository.delete(registro); - } else { - throw new NoDataFoundException("No existe un articulo con ese id"); - } } + private Articulo findByArticleId(int id) { + + return articuloRepository.findById(id).orElseThrow( + () -> new NoDataFoundException("No existe un articulo con ese id: %d".formatted(id))); + } + } diff --git a/src/main/java/pe/edu/utp/service/UsuarioServiceImpl.java b/src/main/java/pe/edu/utp/service/UsuarioServiceImpl.java index a14b7e4..bc7cb7f 100644 --- a/src/main/java/pe/edu/utp/service/UsuarioServiceImpl.java +++ b/src/main/java/pe/edu/utp/service/UsuarioServiceImpl.java @@ -153,8 +153,7 @@ public LoginResponseDto login(LoginRequestDto loginRequestDto) { authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequestDto.getEmail(), loginRequestDto.getPassword())); var usuario=usuarioRepository.findByEmail(loginRequestDto.getEmail()).orElseThrow(); var jwtToken=jwtService.generateToken(usuario); - var refreshJwtToken=jwtService.generateRefreshToken(usuario); - return new LoginResponseDto(usuarioConverter.fromEntity(usuario),jwtToken,refreshJwtToken); + return new LoginResponseDto(usuarioConverter.fromEntity(usuario), jwtToken); } catch (JwtException e) { logger.info(e.getMessage(),e); throw new ValidateServiceException(e.getMessage()); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 496d6d0..ee1dd69 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -19,8 +19,7 @@ spring.flyway.baseline-on-migrate=true spring.security.disabled=${SECURITY_DISABLED:false} -logging.level.org.springframework.messaging=DEBUG -logging.level.org.springframework.web.socket=DEBUG +