Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ public class JwtProperties {

public static final String HEADER_STRING = "Authorization"; // header que será passado ao server com o token

public static final String HEADER_ORIGIN = "X-App-Source"; // header que será passado a origem para validar autenticação (user or admin)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import br.edu.utfpr.pb.ecommerce.server_ecommerce.infra.security.handler.CustomAuthenticationEntryPoint;
import br.edu.utfpr.pb.ecommerce.server_ecommerce.infra.security.handler.CustomAuthenticationFailureHandler;
import br.edu.utfpr.pb.ecommerce.server_ecommerce.service.AuthService;
import br.edu.utfpr.pb.ecommerce.server_ecommerce.service.TranslationService;
import br.edu.utfpr.pb.ecommerce.server_ecommerce.service.impl.alertProduct.IAlertProduct.IAlertProductRequestService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,6 +28,7 @@
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.LocaleResolver;

import java.util.Arrays;
import java.util.List;
Expand All @@ -47,6 +49,9 @@ public class WebSecurity {
private final CustomAccessDeniedHandler accessDeniedHandler;
private final CustomAuthenticationFailureHandler failureHandler;

private final TranslationService translationService;
private final LocaleResolver localeResolver;

@Bean
@SneakyThrows
public SecurityFilterChain filterChain(HttpSecurity http) {
Expand Down Expand Up @@ -86,13 +91,36 @@ public SecurityFilterChain filterChain(HttpSecurity http) {

authorize
// ROTAS PÚBLICAS (permitAll)
.requestMatchers(HttpMethod.POST, "/users", "/shipment/products", "/auth/forgot-password", "/auth/reset-password", "/alerts").permitAll()
.requestMatchers(HttpMethod.GET, "/products/**", "/categories/**", "/payments/**", "/cep/validate/", "/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**", "/auth/validate-reset-token", "/api/health").permitAll()
.requestMatchers("/error/**").permitAll()
.requestMatchers(HttpMethod.POST,
"/users",
"/shipment/products",
"/auth/forgot-password",
"/auth/reset-password",
"/alerts")
.permitAll()

.requestMatchers(HttpMethod.GET,
"/products/**",
"/categories/**",
"/payments/**",
"/cep/validate/",
"/v3/api-docs/**",
"/swagger-ui.html",
"/swagger-ui/**",
"/auth/validate-reset-token",
"/api/health")
.permitAll()

.requestMatchers("/error/**")
.permitAll()

// ROTAS DE ADMIN
// Apenas ADMIN pode gerenciar (criar, editar, deletar) produtos, categorias e pagamentos
.requestMatchers("/products/**", "/categories/**", "/payments/**").hasAnyAuthority("ADMIN");
.requestMatchers(
"/products/**",
"/categories/**",
"/payments/**")
.hasAnyAuthority("ADMIN");

// ROTAS AUTENTICADAS (USER ou ADMIN)
// Qualquer usuário autenticado pode acessar as demais rotas
Expand All @@ -102,7 +130,16 @@ public SecurityFilterChain filterChain(HttpSecurity http) {
);
http.authenticationManager(authenticationManager)
//Filtro da Autenticação - sobrescreve o método padrão do Spring Security para Autenticação.
.addFilter(new JWTAuthenticationFilter(authenticationManager, authService, objectMapper, jwtProperties, failureHandler, alertProductRequestService))
.addFilter(new JWTAuthenticationFilter(
authenticationManager,
authService,
objectMapper,
jwtProperties,
failureHandler,
alertProductRequestService,
translationService,
localeResolver
))
//Filtro da Autorização - - sobrescreve o método padrão do Spring Security para Autorização.
.addFilter(new JWTAuthorizationFilter(authenticationManager, jwtProperties))
//Como será criada uma API REST e todas as requisições que necessitam de autenticação/autorização serão realizadas com o envio do token JWT do usuário, não será necessário fazer controle de sessão no *back-end*.
Expand All @@ -126,7 +163,8 @@ public CorsConfigurationSource corsConfigurationSource() {
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));

// Lista dos Headers autorizados, o Authorization será o header que iremos utilizar para transferir o Token
configuration.setAllowedHeaders(List.of("Authorization", "Content-Type", "Accept", "Origin"));
configuration.setAllowedHeaders(List.of("Authorization", "Content-Type", "Accept", "Origin", "X-App-Source"));
configuration.setExposedHeaders(List.of("Authorization"));

configuration.setAllowCredentials(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import br.edu.utfpr.pb.ecommerce.server_ecommerce.infra.security.exception.JsonAuthenticationException;
import br.edu.utfpr.pb.ecommerce.server_ecommerce.model.User;
import br.edu.utfpr.pb.ecommerce.server_ecommerce.service.AuthService;
import br.edu.utfpr.pb.ecommerce.server_ecommerce.service.TranslationService;
import br.edu.utfpr.pb.ecommerce.server_ecommerce.service.impl.alertProduct.IAlertProduct.IAlertProductRequestService;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
Expand All @@ -16,34 +17,42 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.servlet.LocaleResolver;

import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.stream.Collectors;


public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthService authService;
private final ObjectMapper objectMapper;
private final TranslationService translationService;
private final LocaleResolver localeResolver;
private final JwtProperties jwtProperties;
private final IAlertProductRequestService alertProductRequestService;

public JWTAuthenticationFilter(AuthenticationManager authenticationManager,
AuthService authService,
ObjectMapper objectMapper,
JwtProperties jwtProperties,
AuthenticationFailureHandler authenticationFailureHandler, IAlertProductRequestService alertProductRequestService) {
AuthenticationFailureHandler authenticationFailureHandler,
IAlertProductRequestService alertProductRequestService,
TranslationService translationService,
LocaleResolver localeResolver) {
super(authenticationManager);
this.authService = authService;
this.objectMapper = objectMapper;
this.translationService = translationService;
this.jwtProperties = jwtProperties;
this.localeResolver = localeResolver;
this.alertProductRequestService = alertProductRequestService;

this.setAuthenticationFailureHandler(authenticationFailureHandler);
Expand All @@ -53,15 +62,24 @@ public JWTAuthenticationFilter(AuthenticationManager authenticationManager,
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
String appSource = request.getHeader(JwtProperties.HEADER_ORIGIN);
LoginRequestDTO credentials = objectMapper.readValue(request.getInputStream(), LoginRequestDTO.class);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
credentials.getEmail(),
credentials.getPassword());

return super.getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
credentials.getEmail(),
credentials.getPassword(),
new ArrayList<>()
)
);
Authentication authResult = super.getAuthenticationManager().authenticate(authToken);

if ("admin".equalsIgnoreCase(appSource)) {
boolean isAdmin = authResult.getAuthorities().stream()
.anyMatch(role -> role.getAuthority().equals("ADMIN"));

if (!isAdmin) {
throw new BadCredentialsException(translationService.getMessageLocale("access.denied", localeResolver.resolveLocale(request)));
}
}

return authResult;
} catch (IOException e) {
throw new JsonAuthenticationException("Invalid data format for login request", e);
}
Expand All @@ -77,12 +95,8 @@ protected void successfulAuthentication(HttpServletRequest request,

alertProductRequestService.syncOrphanAlerts(user);

// o método create() da classe JWT é utilizado para criação de um novo token JWT
String token = JWT.create()
// o objeto authResult possui os dados do usuário autenticado, nesse caso o método getId() retorna o id do usuário foi autenticado no método attemptAuthentication.
// mudei para getId() por ser um atributo imutável
.withSubject(user.getId().toString())
//a data de validade do token é a data atual mais o valor armazenado na constante EXPIRATION_TIME, nesse caso 1 dia
.withExpiresAt(
getExpirationDate()
)
Expand Down
6 changes: 3 additions & 3 deletions src/main/resources/application-dev.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# H2 DATABASE
spring.datasource.generate-unique-name=false
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
#spring.datasource.generate-unique-name=false
#spring.h2.console.enabled=true
#spring.h2.console.path=/h2-console

# PostgresSQL DATABASE:
spring.datasource.url=jdbc:postgresql://localhost:5432/riffhouse
Expand Down
Loading