尽管我在配置类中允许了该路径,但当我通过 Postman 发送 POST 请求时,仍然收到 403 禁止。如果我禁用 Spring Security(允许所有请求),该请求会起作用。
我在终端上收到此消息:
2024-12-19T00:21:09.253+01:00 DEBUG 15372 --- [backend-2in1-project] [nio-8080-exec-3] o.s.security.web.FilterChainProxy : Securing POST /api/v1/auth/register
2024-12-19T00:21:09.253+01:00 DEBUG 15372 --- [backend-2in1-project] [nio-8080-exec-3] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2024-12-19T00:21:09.253+01:00 DEBUG 15372 --- [backend-2in1-project] [nio-8080-exec-3] o.s.s.w.a.Http403ForbiddenEntryPoint : Pre-authenticated entry point called. Rejecting access
配置类
package com.momentstock.backend._in1.project.security;
import lombok.RequiredArgsConstructor;
import org.apache.catalina.filters.CorsFilter;
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.core.userdetails.UserDetailsService;
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.client.RestTemplate;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import java.util.List;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
//@EnableGlobalMethodSecurity(prePostEnabled = true) // to enable @preAuthorize
public class SecurityConfig {
private final UserDetailsService userDetailsService;
private final JwtAuthFilter jwtAuthFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.authorizeHttpRequests(con -> con
.requestMatchers("/auth/**").permitAll()
.requestMatchers("/users/**").hasRole("USER")
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/api/v1/orders/payment/callback").permitAll()
.requestMatchers("/welcome/**").permitAll()
// .requestMatchers("/testKey").permitAll()
.requestMatchers(HttpMethod.DELETE, "/users/user/me/delete-account").authenticated()
.anyRequest().authenticated()
)
// .authorizeHttpRequests(auth -> auth .anyRequest().permitAll())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of(
"https://api.flutterwave.com",
"https://api.paystack.co",
"https://sandbox.interswitchng.com"
));
configuration.addAllowedOrigin("*");
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(List.of("*")); // Allow all headers
configuration.setExposedHeaders(List.of("Authorization", "Content-Type")); // Optional, expose specific headers
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration); // Apply to all endpoints
return source;
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public ModelMapper modelmapper(){
return new ModelMapper();
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
控制器:
@RestController
@RequiredArgsConstructor
@RequestMapping("${api.prefix}/auth")
public class UserAuthController {
private final IUserService userService;
private final UserRepository userRepository;
private final VerificationTokenRepository tokenRepository;
private final JwtTokenProvider tokenProvider;
@PostMapping("/register")
public ResponseEntity<String> registerUser(@RequestBody CreateUserRequest request){
userService.registerUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully, check your email complete verification");
}
@PostMapping("/login")
public ResponseEntity<ApiResponse> loginUser(@RequestBody LoginRequest request){
try {
LoginResponse loginResponse = userService.loginUser(request);
return ResponseEntity.ok(new ApiResponse("Success", loginResponse));
} catch (AuthenticationException e) {
return ResponseEntity.status(BAD_REQUEST).body(new ApiResponse(e.getMessage(), null));
}
}
用户详细信息:
package com.momentstock.backend._in1.project.security;
import com.momentstock.backend._in1.project.enums.Role;
import com.momentstock.backend._in1.project.models.User;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@Data
public class UserRegDetails implements UserDetails {
private String userName;
private String password;
private boolean isEnabled;
private Role role;
public UserRegDetails(User user) {
this.userName = user.getEmail();
this.password = user.getPassword();
this.isEnabled = user.isEnabled();
this.role = Role.valueOf(user.getRole().name());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return role != null
? List.of(new SimpleGrantedAuthority(role.name().toUpperCase()))
: List.of();
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
// return UserDetails.super.isAccountNonExpired();
return true;
}
@Override
public boolean isAccountNonLocked() {
// return UserDetails.super.isAccountNonLocked();
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// return UserDetails.super.isCredentialsNonExpired();
return true;
}
@Override
public boolean isEnabled() {
// return UserDetails.super.isEnabled();
return isEnabled;
}
}
服务
@Service
@RequiredArgsConstructor
public class UserRegDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email).map(UserRegDetails::new).orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
}
我有一个配置整个 CORS 主题的新类:
@Configuration
public class CorsConfig implements WebMvcConfigurer {
protected static final String[] CORS_ORIGIN_DOMAIN_PROPERTY_KEYS = {
"cors.allowed.origin",
"cors.allowed.origin.public"
};
@Autowired
private Environment env;
/**
* Configuration of cors.
* @return WebMvcConfigurer
*/
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(getAllAvailableOrigins())
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true);
}
};
}
private String[] getAllAvailableOrigins() {
List < String > originList = new ArrayList < > ();
for (String key: CORS_ORIGIN_DOMAIN_PROPERTY_KEYS) {
String domain = env.getProperty(key);
if (StringUtils.isNotEmpty(domain)) {
String[] domainArray = domain.split(",");
originList.addAll(Arrays.asList(domainArray));
}
}
return originList.toArray(new String[] {});
}
}
其中“cors.allowed.origin”和“cors.allowed.origin.public”是“application.properties”文件中配置的值。其中的值可以是:
cors.allowed.origin=http://localhost:4200,https://something.somethingElse.com
希望对您有帮助,
阿德里安