我的 SecurityConfiguration 和 CustomAuthenticationFilter 之间存在循环依赖关系。我认为 AuthenticationManager 是问题所在,我无法解决它。
安全配置
@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfiguration {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final CredentialsAuthenticationFilter credentialsAuthenticationFilter;
private final CustomUserDetailsService userDetailsService;
private final LogoutHandler logoutHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(Customizer.withDefaults())
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(registry -> registry
.requestMatchers(HttpMethod.POST, "/api/v1/trainees",
"/api/v1/trainers").permitAll()
.anyRequest().authenticated())
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(credentialsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.logout(logout -> logout
.logoutUrl("/api/v1/users/logout")
.addLogoutHandler(logoutHandler)
.logoutSuccessHandler((request, response, authentication) -> {
SecurityContextHolder.clearContext();
}));
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
return daoAuthenticationProvider;
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(List.of("http://localhost:8081"));
corsConfiguration.setAllowedMethods(List.of("OPTIONS", "HEAD", "GET", "PUT", "POST", "DELETE", "PATCH"));
corsConfiguration.setAllowedHeaders(List.of("Authorization"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
}
凭证身份验证过滤器
@Component
public class CredentialsAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final JwtIssuer jwtIssuer;
private final TokenService tokenService;
private final UserService userService;
public CredentialsAuthenticationFilter(
@Lazy AuthenticationManager authenticationManager,
JwtIssuer jwtIssuer,
TokenService tokenService,
UserService userService
) {
this.authenticationManager = authenticationManager;
this.jwtIssuer = jwtIssuer;
this.tokenService = tokenService;
this.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/api/v1/users/login", "POST"));
this.userService = userService;
}
@Override
public Authentication attemptAuthentication(
HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
try {
InputStream requestBody = request.getInputStream();
AuthDTORequest authDTORequest = new ObjectMapper().readValue(requestBody, AuthDTORequest.class);
String username = authDTORequest.getUsername();
String password = authDTORequest.getPassword();
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username, password);
return authenticationManager.authenticate(authenticationToken);
} catch (IOException e) {
throw new SCAuthenticationException("Authentication failed");
}
}
@Override
protected void successfulAuthentication(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult) throws IOException, ServletException {
UserPrincipal principal = (UserPrincipal) authResult.getPrincipal();
String accessToken = jwtIssuer.issue(principal.getId(), principal.getUsername());
UserEntity user = userService.findByUsername(principal.getUsername());
tokenService.revokeAllUserTokens(user.getId());
TokenEntity token = new TokenEntity(
accessToken,
TokenType.BEARER,
false,
false,
user);
tokenService.save(token);
AuthDTOResponse authDTOResponse = new AuthDTOResponse(accessToken);
response.getWriter().write(new ObjectMapper().writeValueAsString(authDTOResponse));
chain.doFilter(request, response);
}
@Override
protected void unsuccessfulAuthentication(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
super.unsuccessfulAuthentication(request, response, failed);
throw new SCAuthenticationException("Authentication failed.");
}
}
我尝试应用惰性注释,但它没有帮助,我收到此错误
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'credentialsAuthenticationFilter' defined in file [/CredentialsAuthenticationFilter.class]: authenticationManager must be specified
根据
AuthenticationConfiguration
,注入的bean中有AuthenticationManagerBuilder
,但是没有AuthenticationManager
。根据HttpSecurity.beforeConfigure()
。 AuthenticationManager
可以从 sharedObject
获得