JWT configuration - CORS Invalid request

Discussão em 'Angular' iniciado por Hanae Melehi, Dezembro 1, 2024.

  1. Hanae Melehi

    Hanae Melehi Guest

    I'm implementing the authentication of a SpringBoot, Angular app (that uses a PostgresSQL DB docker container) using JWT, I'm using http, not https not sure if this can cause a probleme, the authentication in the backend side seems to be fine, but when i try login in from browser the POST request response from the server is 200, but I get Invalid CORS request. Can anyone HELP Please!!!

    package fr.makizart.restserver;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    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.web.cors.CorsConfiguration;
    import org.springframework.web.cors.CorsConfigurationSource;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

    import java.net.InetAddress;
    import java.net.UnknownHostException;
    import java.util.Arrays;

    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig {

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
    AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
    authenticationManagerBuilder
    .userDetailsService(userDetailsService)
    .passwordEncoder(passwordEncoder());
    return authenticationManagerBuilder.build();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
    JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager);
    http
    .cors(cors -> cors.configurationSource(corsConfigurationSource())) // Updated CORS configuration usage
    .csrf(csrf -> csrf.disable())
    .authorizeHttpRequests(auth -> auth
    // .requestMatchers("/login", "/api/login").permitAll() // Autoriser l'accès public aux endpoints de login
    .requestMatchers("/api/login").permitAll()
    .requestMatchers("/admin/**").hasRole("ADMIN")
    .anyRequest().permitAll()
    )
    .formLogin(form -> form
    .loginPage("/#/login")
    .defaultSuccessUrl("/#/admin", true)
    .permitAll()
    )
    .logout(logout -> logout
    .logoutUrl("/logout")
    .logoutSuccessUrl("/#/login")
    .permitAll()
    )
    .httpBasic(httpBasic -> httpBasic.realmName("MyApp"))
    .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

    return http.build();
    }

    // CORS configuration bean


    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();

    // Configuration des origines autorisées
    configuration.setAllowedOrigins(Arrays.asList(
    // "http://localhost:4200", // Localhost pour Angular
    // "http://172.18.0.1:4200", // IP de votre front-end Angular
    "http://172.18.0.1:8080" // IP de votre back-end Spring Boot
    ));


    configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
    configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
    configuration.setExposedHeaders(Arrays.asList("Authorization"));
    configuration.setAllowCredentials(true); // Autorise les cookies et les headers comme les tokens

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
    }


    Also this is JwtAuthenticationFilter implementation:

    package fr.makizart.restserver;

    import com.fasterxml.jackson.databind.ObjectMapper;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

    import jakarta.servlet.FilterChain;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;

    public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;
    private final String secretKey = "YourSecretKey"; // Replace with your secret key

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
    this.authenticationManager = authenticationManager;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    try {
    // Extract username and password from request
    Map<String, String> credentials = new ObjectMapper().readValue(request.getInputStream(), HashMap.class);
    String username = credentials.get("username");
    String password = credentials.get("password");

    // Authenticate the user
    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
    return authenticationManager.authenticate(authenticationToken);
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
    // Generate JWT token
    User user = (User) authResult.getPrincipal();
    String token = Jwts.builder()
    .setSubject(user.getUsername())
    .setExpiration(new Date(System.currentTimeMillis() + 864_000_000)) // 10 days
    .signWith(SignatureAlgorithm.HS512, secretKey.getBytes())
    .compact();

    // Create response body
    Map<String, String> responseBody = new HashMap<>();
    responseBody.put("token", "Bearer " + token);
    responseBody.put("message", "Authentication successful");

    System.out.println("Token généré : " + token);

    // Set response content type to JSON
    response.setContentType("application/json");
    response.setCharacterEncoding("UTF-8");

    // Write response body
    response.getWriter().write(new ObjectMapper().writeValueAsString(responseBody));
    }
    }


    and on Angular side this is the auth.service.ts's implementation:

    // `makizapp/WebView/site/src/app/services/auth.service.ts`
    import { Injectable } from '@angular/core';
    import { HttpClient, HttpHeaders } from '@angular/common/http';
    import { Observable } from 'rxjs';
    import { tap } from 'rxjs/operators';

    @Injectable({
    providedIn: 'root',
    })
    export class AuthService {
    private loginUrl = 'http://172.18.0.1:8080/api/login'; // URL of the backend API

    constructor(private http: HttpClient) {}

    login(username: string, password: string): Observable<any> {
    return this.http.post<any>(this.loginUrl, { username, password },
    {
    headers: new HttpHeaders({
    'Content-Type': 'application/json',
    }),
    withCredentials: true, // Inclut les credentials dans les requêtes
    }).pipe(
    tap((response: any) => {
    /* localStorage.setItem('token', response.token);
    })
    );*/
    if (response && response.token) {
    localStorage.setItem('token', response.token);
    } else {
    console.error('Token non reçu dans la réponse');
    }
    }),
    catchError((error) => {
    console.error('Erreur lors du login :', error);
    return throwError(error);
    })

    );

    }

    isLoggedIn(): boolean {
    return !!localStorage.getItem('token');
    }

    logout(): void {
    localStorage.removeItem('token');
    }

    getAuthHeaders(): HttpHeaders {
    const token = localStorage.getItem('token');
    return new HttpHeaders({
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`
    });
    }
    }


    I am also serving the frontend by the backend server, on the same port.

    Continue reading...

Compartilhe esta Página