JWT 配置 - CORS 无效请求

问题描述 投票:0回答:1

我正在使用 JWT 实现 SpringBoot、Angular 应用程序(使用 PostgresSQL DB docker 容器)的身份验证,我使用的是 http,而不是 https 不确定这是否会导致问题,后端的身份验证似乎没问题,但是当我尝试从浏览器登录时,来自服务器的 POST 请求响应为 200,但我收到无效的 CORS 请求。任何人都可以帮忙吗!!!

 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;
    }

这也是 JwtAuthenticationFilter 的实现:

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));
    }
}

在 Angular 方面,这是 auth.service.ts 的实现:

   // `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}`
    });
  }
}

我还在同一端口上通过后端服务器为前端提供服务。

angular spring-boot authentication jwt
1个回答
0
投票

您可以创建一个配置类来处理您的 cors 配置,如下所示:

@Configuration
@EnableWebMvc
public class corsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins(String.valueOf(List.of(
                        // "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
                )))
                .allowedMethods(String.valueOf(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")))
                .allowedHeaders(String.valueOf(Arrays.asList("Authorization", "Content-Type")))
                .allowCredentials(true);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.