为什么Spring Security将空密码提供给密码编码器?

时间:2019-05-31 15:31:23

标签: spring spring-boot spring-security bcrypt password-encryption

我正在使用带有BCrypt密码编码器的spring security进行身份验证。当我想登录时,Spring security会使用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");
    }
}

userDetailsS​​ervise:

@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='$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将空字符串作为密码编码器提供给密码编码器。

@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='$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

1 个答案:

答案 0 :(得分:0)

BCryptPasswordEncoder会将加载的用户的密码与登录表单中输入的密码进行比较,以查看它们是否匹配。如果前者为null,它将发出Empty encoded password警告。

因此,通过查看如何加载用户,您的UserDetailsService将始终返回带有a null passwordUser,因为您在getPassword()中对其进行了硬编码(Btw ,getUsername()也有同样的问题)。因此,更改以下内容即可解决此问题:

@Override
public String getPassword() {
    return this.password;
}

@Override
public String getUsername() {
    return this.username;
}