为什么我能够使用 Java Spring 中的密码哈希进行身份验证安全
Why am i able to authenticate with the password hash in Java Spring Security
我正在使用 spring-boot-starter-security
。我将 WebSecurityConfigation
配置为使用 DaoAuthenticationProvider
提供商和 BCryptPasswordEncoder
进行身份验证。 UserDetailsService
实现 returns 一个 User
对象,password
字段设置为实际哈希值。
它似乎工作正常。但是我注意到我可以使用密码 或 哈希成功进行身份验证。
例如,密码本身是一个生成的 UUID 51a80a6a-8618-4583-98d2-d77d103a62c6
,它被编码为 a$u4OSZf7B9yJvQ5UYNNpy7O4f3g0gfUMl2Xmm3h282W.3emSN3WqxO
。
完整的网络安全配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DemoUserDetailsService userDetailsService;
@Autowired
private DaoAuthenticationProvider authenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.userDetailsService(userDetailsService);
auth.inMemoryAuthentication().withUser("user").password("password").roles("SUPER", "BASIC");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasRole("BASIC").and().httpBasic();
http.csrf().disable();
}
}
@Service
public class DemoUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
UserDAO userDAO = userRepository.findByEmailAndActivated(email);
if (userDAO == null) {
throw new UsernameNotFoundException(String.format("Email %s not found", email));
}
return new User(email, userDAO.getPasswordHash(), getGrantedAuthorities(email));
}
private Collection<? extends GrantedAuthority> getGrantedAuthorities(String email) {
return asList(() -> "ROLE_BASIC");
}
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
为什么我可以使用两个字符串进行身份验证?我做错了什么,或者这是预期的还是某些配置?我无法在文档中找到任何内容。
最好的方法是连接调试器。使用PasswordEncoder匹配密码的实际逻辑在DaoAuthenticationProvider
的additionalAuthenticationChecks
方法中
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
Object salt = null;
if (this.saltSource != null) {
salt = this.saltSource.getSalt(userDetails);
}
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
}
String presentedPassword = authentication.getCredentials().toString();
if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
}
}
这委托给 BCryptPasswordEncoder
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (encodedPassword == null || encodedPassword.length() == 0) {
logger.warn("Empty encoded password");
return false;
}
if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
logger.warn("Encoded password does not look like BCrypt");
return false;
}
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
我猜这是因为根据您的配置,您实际上得到了两个 DaoAuthenticationProvider
。一种是显式的,由您配置,一种是隐式的,在您调用 auth.userDetailsService(userDetailsService);
时在后台配置,并且对于这个隐式提供程序,您没有设置密码编码器。
试试这个:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
auth.inMemoryAuthentication().withUser("user").password("password").roles("SUPER", "BASIC");
}
并删除您手动配置的提供程序 - 似乎您实际上并不需要它。
希望对您有所帮助。
我正在使用 spring-boot-starter-security
。我将 WebSecurityConfigation
配置为使用 DaoAuthenticationProvider
提供商和 BCryptPasswordEncoder
进行身份验证。 UserDetailsService
实现 returns 一个 User
对象,password
字段设置为实际哈希值。
它似乎工作正常。但是我注意到我可以使用密码 或 哈希成功进行身份验证。
例如,密码本身是一个生成的 UUID 51a80a6a-8618-4583-98d2-d77d103a62c6
,它被编码为 a$u4OSZf7B9yJvQ5UYNNpy7O4f3g0gfUMl2Xmm3h282W.3emSN3WqxO
。
完整的网络安全配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DemoUserDetailsService userDetailsService;
@Autowired
private DaoAuthenticationProvider authenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.userDetailsService(userDetailsService);
auth.inMemoryAuthentication().withUser("user").password("password").roles("SUPER", "BASIC");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasRole("BASIC").and().httpBasic();
http.csrf().disable();
}
}
@Service
public class DemoUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
UserDAO userDAO = userRepository.findByEmailAndActivated(email);
if (userDAO == null) {
throw new UsernameNotFoundException(String.format("Email %s not found", email));
}
return new User(email, userDAO.getPasswordHash(), getGrantedAuthorities(email));
}
private Collection<? extends GrantedAuthority> getGrantedAuthorities(String email) {
return asList(() -> "ROLE_BASIC");
}
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
为什么我可以使用两个字符串进行身份验证?我做错了什么,或者这是预期的还是某些配置?我无法在文档中找到任何内容。
最好的方法是连接调试器。使用PasswordEncoder匹配密码的实际逻辑在DaoAuthenticationProvider
additionalAuthenticationChecks
方法中
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
Object salt = null;
if (this.saltSource != null) {
salt = this.saltSource.getSalt(userDetails);
}
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
}
String presentedPassword = authentication.getCredentials().toString();
if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
}
}
这委托给 BCryptPasswordEncoder
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (encodedPassword == null || encodedPassword.length() == 0) {
logger.warn("Empty encoded password");
return false;
}
if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
logger.warn("Encoded password does not look like BCrypt");
return false;
}
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
我猜这是因为根据您的配置,您实际上得到了两个 DaoAuthenticationProvider
。一种是显式的,由您配置,一种是隐式的,在您调用 auth.userDetailsService(userDetailsService);
时在后台配置,并且对于这个隐式提供程序,您没有设置密码编码器。
试试这个:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
auth.inMemoryAuthentication().withUser("user").password("password").roles("SUPER", "BASIC");
}
并删除您手动配置的提供程序 - 似乎您实际上并不需要它。
希望对您有所帮助。