为什么 spring security 给密码编码器一个空密码?
why does spring security give empty password to password encoder?
我正在使用 spring 安全和 BCrypt 密码编码器进行身份验证。当我想登录时,Spring 安全性使用 JPA 正确获取用户数据,但是为了使用编码密码检查原始密码,它将空字符串作为编码密码提供给密码编码器。
spring 安全配置:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("userDetailsServiceImpl")
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder(){
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").hasAuthority("USER")
.antMatchers("/css/**","/font/**","/js/**","/image/**").permitAll()
.antMatchers("/register").permitAll()
.and().formLogin().loginPage("/login").successForwardUrl("/").permitAll()
.and().logout().logoutSuccessUrl("/login");
}
}
用户详细信息服务:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository userRepo;
@Autowired
public UserDetailsServiceImpl(UserRepository userRepo){
this.userRepo = userRepo;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepo.findByUsername(username);
if(user != null) {
System.out.println(user.toString());
return user;
}
throw new UsernameNotFoundException("User "+ username +" not found");
}
}
用户实体:
@Entity
@Table(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
@SequenceGenerator(name = "user_seq", sequenceName = "users_id_seq",allocationSize = 1)
private long id;
@NotEmpty
@Column(nullable = false, unique=true)
private String username;
@Email
@Column(nullable = false, updatable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private boolean enabled;
@CreationTimestamp
@Column(name = "creation_time")
private Timestamp creationTime;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(joinColumns = @JoinColumn(name="user_id"))
private List<Authority> authorities;
...
日志:
2019-05-31 19:51:58.888 DEBUG 7181 --- [nio-8080-exec-3] w.a.UsernamePasswordAuthenticationFilter : Request is to process authentication
2019-05-31 19:51:58.889 DEBUG 7181 --- [nio-8080-exec-3] o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2019-05-31 19:51:59.245 DEBUG 7181 --- [nio-8080-exec-3] tor$SharedEntityManagerInvocationHandler : Creating new EntityManager for shared EntityManager invocation
2019-05-31 19:51:59.388 INFO 7181 --- [nio-8080-exec-3] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select user0_.id as id1_1_, user0_.creation_time as creation2_1_, user0_.email as email3_1_, user0_.enabled as enabled4_1_, user0_.password as password5_1_, user0_.username as username6_1_ from users user0_ where user0_.username=?
Hibernate: select authoritie0_.user_id as user_id1_0_0_, authoritie0_.authorities as authorit2_0_0_ from user_authorities authoritie0_ where authoritie0_.user_id=?
User{
id=8
, username='username'
, email='mail@example.com'
, password='a$TyQ8x0KUP9Nrtzo2ljfNfei4S3h1kFpYEb5/CDs2lKSDzMJECQ./q'
, enabled=true
, creationTime=2019-05-31 19:32:12.948
, authorities=[USER]}
2019-05-31 19:51:59.758 WARN 7181 --- [nio-8080-exec-3] o.s.s.c.bcrypt.BCryptPasswordEncoder : Empty encoded password
2019-05-31 19:51:59.758 DEBUG 7181 --- [nio-8080-exec-3] o.s.s.a.dao.DaoAuthenticationProvider : Authentication failed: password does not match stored value
2019-05-31 19:51:59.773 DEBUG 7181 --- [nio-8080-exec-3] w.a.UsernamePasswordAuthenticationFilter : Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
我创建了以下密码编码器,我发现 spring 安全性将空字符串作为密码编码器的编码密码。
@Component
public class MyPasswordEncoder implements PasswordEncoder {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
@Override
public String encode(CharSequence rawPassword) {
return encoder.encode(rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
System.out.println("raw password: " + rawPassword + "\t encoded passwod: "+ encodedPassword);
return encoder.matches(rawPassword,encodedPassword);
}
}
带有自定义密码编码器的日志:
2019-06-01 03:19:52.759 DEBUG 7870 --- [nio-8080-exec-5] w.a.UsernamePasswordAuthenticationFilter : Request is to process authentication
2019-06-01 03:19:52.759 DEBUG 7870 --- [nio-8080-exec-5] o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2019-06-01 03:19:53.036 DEBUG 7870 --- [nio-8080-exec-5] tor$SharedEntityManagerInvocationHandler : Creating new EntityManager for shared EntityManager invocation
2019-06-01 03:19:53.095 INFO 7870 --- [nio-8080-exec-5] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select user0_.id as id1_1_, user0_.creation_time as creation2_1_, user0_.email as email3_1_, user0_.enabled as enabled4_1_, user0_.password as password5_1_, user0_.username as username6_1_ from users user0_ where user0_.username=?
Hibernate: select authoritie0_.user_id as user_id1_0_0_, authoritie0_.authorities as authorit2_0_0_ from user_authorities authoritie0_ where authoritie0_.user_id=?
User{
id=8
, username='username'
, email='mail@example.com'
, password='a$TyQ8x0KUP9Nrtzo2ljfNfei4S3h1kFpYEb5/CDs2lKSDzMJECQ./q'
, enabled=true
, creationTime=2019-05-31 19:32:12.948
, authorities=[USER]}
raw password: password encoded passwod: null
2019-06-01 03:19:53.413 WARN 7870 --- [nio-8080-exec-5] o.s.s.c.bcrypt.BCryptPasswordEncoder : Empty encoded password
2019-06-01 03:19:53.413 DEBUG 7870 --- [nio-8080-exec-5] o.s.s.a.dao.DaoAuthenticationProvider : Authentication failed: password does not match stored value
2019-06-01 03:19:53.415 DEBUG 7870 --- [nio-8080-exec-5] w.a.UsernamePasswordAuthenticationFilter : Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
github 上的完整来源:https://github.com/yrostami/spring_sample
BCryptPasswordEncoder
会将加载用户的密码与从登录表单输入的密码进行比较,看它们是否匹配。如果前者为 null ,它将给出 Empty encoded password
警告。
因此,通过查看您如何加载用户,事实证明您的 UserDetailsService
将始终 return 一个 User
和 a null password,因为您将其硬编码在 getPassword()
(顺便说一下,getUsername()
也有同样的问题)。所以更改为以下内容应该可以修复它:
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
我正在使用 spring 安全和 BCrypt 密码编码器进行身份验证。当我想登录时,Spring 安全性使用 JPA 正确获取用户数据,但是为了使用编码密码检查原始密码,它将空字符串作为编码密码提供给密码编码器。
spring 安全配置:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("userDetailsServiceImpl")
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder(){
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").hasAuthority("USER")
.antMatchers("/css/**","/font/**","/js/**","/image/**").permitAll()
.antMatchers("/register").permitAll()
.and().formLogin().loginPage("/login").successForwardUrl("/").permitAll()
.and().logout().logoutSuccessUrl("/login");
}
}
用户详细信息服务:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository userRepo;
@Autowired
public UserDetailsServiceImpl(UserRepository userRepo){
this.userRepo = userRepo;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepo.findByUsername(username);
if(user != null) {
System.out.println(user.toString());
return user;
}
throw new UsernameNotFoundException("User "+ username +" not found");
}
}
用户实体:
@Entity
@Table(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
@SequenceGenerator(name = "user_seq", sequenceName = "users_id_seq",allocationSize = 1)
private long id;
@NotEmpty
@Column(nullable = false, unique=true)
private String username;
@Email
@Column(nullable = false, updatable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private boolean enabled;
@CreationTimestamp
@Column(name = "creation_time")
private Timestamp creationTime;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(joinColumns = @JoinColumn(name="user_id"))
private List<Authority> authorities;
...
日志:
2019-05-31 19:51:58.888 DEBUG 7181 --- [nio-8080-exec-3] w.a.UsernamePasswordAuthenticationFilter : Request is to process authentication
2019-05-31 19:51:58.889 DEBUG 7181 --- [nio-8080-exec-3] o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2019-05-31 19:51:59.245 DEBUG 7181 --- [nio-8080-exec-3] tor$SharedEntityManagerInvocationHandler : Creating new EntityManager for shared EntityManager invocation
2019-05-31 19:51:59.388 INFO 7181 --- [nio-8080-exec-3] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select user0_.id as id1_1_, user0_.creation_time as creation2_1_, user0_.email as email3_1_, user0_.enabled as enabled4_1_, user0_.password as password5_1_, user0_.username as username6_1_ from users user0_ where user0_.username=?
Hibernate: select authoritie0_.user_id as user_id1_0_0_, authoritie0_.authorities as authorit2_0_0_ from user_authorities authoritie0_ where authoritie0_.user_id=?
User{
id=8
, username='username'
, email='mail@example.com'
, password='a$TyQ8x0KUP9Nrtzo2ljfNfei4S3h1kFpYEb5/CDs2lKSDzMJECQ./q'
, enabled=true
, creationTime=2019-05-31 19:32:12.948
, authorities=[USER]}
2019-05-31 19:51:59.758 WARN 7181 --- [nio-8080-exec-3] o.s.s.c.bcrypt.BCryptPasswordEncoder : Empty encoded password
2019-05-31 19:51:59.758 DEBUG 7181 --- [nio-8080-exec-3] o.s.s.a.dao.DaoAuthenticationProvider : Authentication failed: password does not match stored value
2019-05-31 19:51:59.773 DEBUG 7181 --- [nio-8080-exec-3] w.a.UsernamePasswordAuthenticationFilter : Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
我创建了以下密码编码器,我发现 spring 安全性将空字符串作为密码编码器的编码密码。
@Component
public class MyPasswordEncoder implements PasswordEncoder {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
@Override
public String encode(CharSequence rawPassword) {
return encoder.encode(rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
System.out.println("raw password: " + rawPassword + "\t encoded passwod: "+ encodedPassword);
return encoder.matches(rawPassword,encodedPassword);
}
}
带有自定义密码编码器的日志:
2019-06-01 03:19:52.759 DEBUG 7870 --- [nio-8080-exec-5] w.a.UsernamePasswordAuthenticationFilter : Request is to process authentication
2019-06-01 03:19:52.759 DEBUG 7870 --- [nio-8080-exec-5] o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2019-06-01 03:19:53.036 DEBUG 7870 --- [nio-8080-exec-5] tor$SharedEntityManagerInvocationHandler : Creating new EntityManager for shared EntityManager invocation
2019-06-01 03:19:53.095 INFO 7870 --- [nio-8080-exec-5] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select user0_.id as id1_1_, user0_.creation_time as creation2_1_, user0_.email as email3_1_, user0_.enabled as enabled4_1_, user0_.password as password5_1_, user0_.username as username6_1_ from users user0_ where user0_.username=?
Hibernate: select authoritie0_.user_id as user_id1_0_0_, authoritie0_.authorities as authorit2_0_0_ from user_authorities authoritie0_ where authoritie0_.user_id=?
User{
id=8
, username='username'
, email='mail@example.com'
, password='a$TyQ8x0KUP9Nrtzo2ljfNfei4S3h1kFpYEb5/CDs2lKSDzMJECQ./q'
, enabled=true
, creationTime=2019-05-31 19:32:12.948
, authorities=[USER]}
raw password: password encoded passwod: null
2019-06-01 03:19:53.413 WARN 7870 --- [nio-8080-exec-5] o.s.s.c.bcrypt.BCryptPasswordEncoder : Empty encoded password
2019-06-01 03:19:53.413 DEBUG 7870 --- [nio-8080-exec-5] o.s.s.a.dao.DaoAuthenticationProvider : Authentication failed: password does not match stored value
2019-06-01 03:19:53.415 DEBUG 7870 --- [nio-8080-exec-5] w.a.UsernamePasswordAuthenticationFilter : Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
github 上的完整来源:https://github.com/yrostami/spring_sample
BCryptPasswordEncoder
会将加载用户的密码与从登录表单输入的密码进行比较,看它们是否匹配。如果前者为 null ,它将给出 Empty encoded password
警告。
因此,通过查看您如何加载用户,事实证明您的 UserDetailsService
将始终 return 一个 User
和 a null password,因为您将其硬编码在 getPassword()
(顺便说一下,getUsername()
也有同样的问题)。所以更改为以下内容应该可以修复它:
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}