问题描述
我目前正在许多Springboot教程中类似地找到的UserDetailsServiceImpl java类中编码loadUserByUsername方法。问题出在最后一行
return new org.springframework.security.core.userdetails.User(user.getEmail(),user.getpassword(),grantedAuthorities);
根据春季安全性文档,user.getpassword()应该返回一个字符串,但是我使用的是bcrypt和MysqL,因此,当我存储密码时,我将它以二进制(60)的形式存储在MysqL中,当我读取它时从数据库进入用户类,然后被读入
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class users {
@Id
private String email;
private long phone_number;
private String first_name;
private String last_name;
private byte[] password;
private int gender;
}
我的用户类别中的字段。如果我将其转换为字符串,我已经读到它弄乱了密码,但是如果我不这样做,则该函数将不起作用,因为我传递的是byte []而不是字符串。如何在保持此功能的同时保持bcrypt的安全性?
因为在MysqL文档中,它将BINARY(60)映射到byte []此处https://dev.mysql.com/doc/ndbapi/en/mccj-using-clusterj-mappings.html
每个人都说要在此处将bcrypt存储为二进制(60) What column type/length should I use for storing a Bcrypt hashed password in a Database?
解决方法
您可以仅将哈希密码作为字符串返回。无需返回普通密码。实际上,没有人会期望您。
这里的幕后有很多Spring魔术,并且有一个默认的密码编码器/解码器,例如您可以使用PasswordEncoderFactories.createDelegatingPasswordEncoder()
获取密码编码器,该编码器还具有更多自定义选项。当用户登录时,将从UserDetails.getPassword()
返回的散列密码与用户用于登录的密码的散列版本进行比较
我不太熟悉mysql数据类型,但是如果让Spring JPA管理用户实体,并且它具有一个密码字段,该字段使用此PasswordEncoderFactories.createDelegatingPasswordEncoder()
进行编码,然后另存为String,数据库将只是varchar或在MySql中调用的任何类型。默认情况下,此密码编码器使用BCrypt,但您也可以将其配置为使用其他哈希算法。
根据第一个答案,您必须将数据库中的哈希密码与用户使用passwordEncoder.matches(passwordFromUser,encodedPassword)
发送的密码进行比较。
您可以创建一个Bean
来轻松漂亮地将passwordEncoder
注入到UserDetailsService
中。
将bcrypt存储为binary(60)大多是有争议的。根据我的经验,我会推荐varchar
。由于您在保存之前创建了一个密码哈希,因此以后将其与用户提供的密码进行比较就不会成为问题(显然,您也必须对这个密码进行哈希处理)。
您说的是
如果我将其转换为字符串,我已经读到它弄乱了密码
我认为可以通过使用mysql it self的cast
来避免这种情况。尽管它绕来绕去没有任何意义。
春季5引入了DelegatingPasswordEncoder
。一个密码编码器,它基于前缀标识符委派给另一个PasswordEncoder。
例如,您可以:
class DefaultPasswordEncoderFactories {
//the passwords must be of form: {bcrypt}xxx in the db
static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "bcrypt";
Map<String,PasswordEncoder> encoders = new HashMap<>();
encoders.put(encodingId,new BCryptPasswordEncoder());
encoders.put("pbkdf2",new Pbkdf2PasswordEncoder());
encoders.put("scrypt",new SCryptPasswordEncoder());
DelegatingPasswordEncoder delegatingPasswordEncoder = new DelegatingPasswordEncoder(encodingId,encoders);
delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(new BCryptPasswordEncoder(12));
return delegatingPasswordEncoder;
}
}
例如,可以在DaoAuthenticationProvider
中轻松使用它:
private final PasswordEncoder passwordEncoder = DefaultPasswordEncoderFactories.createDelegatingPasswordEncoder();
...
@Bean
public DaoAuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(ethUserDetailsService);
provider.setPasswordEncoder(this.passwordEncoder);
return provider;
}
检查以下链接,除了您提供的链接外,它可能很有用 What data type to use for hashed password field and what length?