我正在使用 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}`
});
}
}
我还在同一端口上通过后端服务器为前端提供服务。
您可以创建一个配置类来处理您的 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);
}
}