问题描述
我正在使用带有BCrypt密码编码器的spring security进行身份验证.当我想登录时,Spring security会使用JPA正确获取用户数据,但是为了检查带有编码密码的原始密码,它会将空字符串作为编码密码提供给密码编码器.
I am using spring security with BCrypt Password encoder for authentication. when i want to login, Spring security fetchs user data with JPA correctly but for checking raw password with encoded password it gives null string as encoded password to password encoder.
spring安全配置:
spring security config :
@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");
}
}
userDetailsServise:
userDetailsServise :
@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='[email protected]'
, password='$2a$10$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 Security将空字符串作为密码编码器提供给密码编码器.
I create following password encoder and I figure out that spring security gives a null string as encoded password to password encoder.
@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);
}
}
带有自定义密码编码器的日志:
logs with custom password encoder:
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='[email protected]'
, password='$2a$10$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
full source on github : https://github.com/yrostami/spring_sample
推荐答案
BCryptPasswordEncoder
会将加载的用户的密码与登录表单中输入的密码进行比较,以查看它们是否匹配.如果前者为null,它将给出Empty encoded password
警告.
BCryptPasswordEncoder
will compare the password from the loaded user with the password entered from the login form to see if they match. If the former is null , it will give Empty encoded password
warning.
因此,通过查看如何加载用户,事实证明您的UserDetailsService
将始终返回带有空密码,因为您在getPassword()
中对其进行了硬编码(Btw,getUsername()
也有同样的问题).因此,更改为以下内容即可解决此问题:
So by looking at how you load the users , it turns out that your UserDetailsService
will always return an User
with a null password because you hardcode it in getPassword()
(Btw, getUsername()
also has the same problem). So change to following should fix it :
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
这篇关于为什么Spring Security将空密码提供给密码编码器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!