У меня есть веб-приложение, которое использует Spring Boot и Security, настроенное на использование входа в форму с аутентификацией JDB C.
Вход и выход из системы работают нормально, и в целом аутентификация работает.
За исключением одного случая ... когда я пытаюсь изменить свой пароль, я замечаю, что хотя само изменение пароля происходит успешно, AuthenticationManager, в котором я хочу проверить существующий пароль ..., имеет значение null!
Как я могу настроить AuthenticationManager (возможно, с DaoAuthenticationProvider и / или DaoAuthenticationManager?), Чтобы AuthenticationManager не был нулевым и проверил существующий пароль?
Соответствующая конфигурация:
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private RESTLogoutSuccessHandler restLogoutSuccessHandler;
@Autowired
private DataSource dataSource;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/h2-console/**")
.permitAll();
httpSecurity.authorizeRequests().antMatchers("/auth/**").authenticated();
httpSecurity.cors().configurationSource(corsConfigurationSource());
httpSecurity.csrf()
.ignoringAntMatchers("/h2-console/**")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
httpSecurity.headers()
.frameOptions()
.sameOrigin();
httpSecurity.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
httpSecurity.formLogin().successHandler(authenticationSuccessHandler);
httpSecurity.formLogin().failureHandler(authenticationFailureHandler);
httpSecurity.logout().logoutSuccessHandler(restLogoutSuccessHandler);
}
@Autowired
@Bean
public UserDetailsManager configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
JdbcUserDetailsManagerConfigurer jdbcUserDetailsManagerConfigurer = auth.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema();
jdbcUserDetailsManagerConfigurer.withUser("user1")
.password(passwordEncoder().encode("user1"))
.roles("USER");
return jdbcUserDetailsManagerConfigurer.getUserDetailsService();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // Strength increased as per OWASP Password Storage Cheat Sheet
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT"));
configuration.setAllowedHeaders(List.of("X-XSRF-TOKEN", "Content-Type"));
configuration.setExposedHeaders(List.of("Content-Disposition"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
source.registerCorsConfiguration("/login", configuration);
source.registerCorsConfiguration("/logout", configuration);
return source;
}
}
AuthController, и здесь я хочу, чтобы UserDetailsManager специально вводился - чтобы можно было легко сменить пароль учетной записи:
import org.adventure.inbound.ChangePasswordData;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
@RestController
@CrossOrigin(origins = "http://localhost:4200")
@RequestMapping("/auth")
public class AuthController {
private UserDetailsManager userDetailsManager;
private PasswordEncoder passwordEncoder;
public AuthController(UserDetailsManager userDetailsManager, PasswordEncoder passwordEncoder) {
this.userDetailsManager = userDetailsManager;
this.passwordEncoder = passwordEncoder;
}
@PreAuthorize("hasRole('USER')")
@PutMapping(path = "changePassword", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> changePassword(@RequestBody ChangePasswordData changePasswordData, Principal principal) {
if (principal == null) {
return ResponseEntity.badRequest().body("Only logged in users may change their password");
} else {
if (changePasswordData.getCurrentPassword() == null || changePasswordData.getNewPassword() == null) {
return ResponseEntity.badRequest().body("Either of the supplied passwords was null");
} else {
String encodedPassword = passwordEncoder.encode(changePasswordData.getNewPassword());
userDetailsManager.changePassword(
changePasswordData.getCurrentPassword(), encodedPassword);
return ResponseEntity.ok().build();
}
}
}
}
Если я попытаюсь Конфигурация, упомянутая ниже в ответе, я получаю:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in org.adventure.controllers.AuthController required a
bean of type 'org.springframework.security.provisioning.UserDetailsManager' that
could not be found.
The following candidates were found but could not be injected:
- Bean method 'inMemoryUserDetailsManager' in
'UserDetailsServiceAutoConfiguration' not loaded because @ConditionalOnBean
(types: org.springframework.security.authentication.AuthenticationManager,
org.springframework.security.authentication.AuthenticationProvider,
org.springframework.security.core.userdetails.UserDetailsService,
org.springframework.security.oauth2.jwt.JwtDecoder
org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
SearchStrategy: all) found beans of type
'org.springframework.security.core.userdetails.UserDetailsService' userDetailsServiceBean