57

从 Spring Security 3.1.4.RELEASE 开始,旧版本org.springframework.security.authentication.encoding.PasswordEncoder 已被弃用,取而代之的是org.springframework.security.crypto.password.PasswordEncoder. 由于我的应用程序尚未向公众发布,我决定迁移到新的、未弃用的 API。

到目前为止,我有一个ReflectionSaltSource自动使用用户的注册日期作为密码的每个用户的盐。

String encodedPassword = passwordEncoder.encodePassword(rawPassword, saltSource.getSalt(user));

在登录过程中,Spring 还使用我的 bean 来适当地验证用户是否可以登录。我无法在新密码编码器中实现这一点,因为 SHA-1 的默认实现 -StandardPasswordEncoder只能添加全局编码器创建期间的秘密盐。

是否有任何合理的方法可以使用未弃用的 API 进行设置?

4

4 回答 4

58

如果您实际上没有使用现有格式注册任何用户,那么您最好改用BCrypt 密码编码器

它少了很多麻烦,因为您根本不必担心盐 - 细节完全封装在编码器中。使用 BCrypt 比使用普通哈希算法更强大,它也是一个与使用其他语言的应用程序兼容的标准。

真的没有理由为新应用程序选择任何其他选项。

于 2013-07-03T14:09:29.607 回答
19

这是对我有用的 BCrypt 的实现。

在 spring-security.xml 中

<authentication-manager >
    <authentication-provider ref="authProvider"></authentication-provider>  
    </authentication-manager>
<beans:bean id="authProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
  <beans:property name="userDetailsService" ref="userDetailsServiceImpl" />
  <beans:property name="passwordEncoder" ref="encoder" />
</beans:bean>
<!-- For hashing and salting user passwords -->
    <beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

在java类中

PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(yourpassword);

更详细的spring security示例点击这里

希望这会有所帮助。

谢谢

于 2013-08-07T13:06:28.007 回答
5

我有一个类似的问题。我需要保留旧的加密密码(Base64/SHA-1/Random salt Encoded),因为用户不想更改密码或重新注册。但是我也想使用BCrypt编码器。

我的解决方案是编写一个定制的解码器,检查在匹配之前首先使用哪种加密方法(BCrypted以 开头$)。

为了解决盐问题,我通过修改后的用户对象将盐 + 加密密码的串联字符串传递给解码器。

解码器

@Component
public class LegacyEncoder implements PasswordEncoder {

    private static final String BCRYP_TYPE = "$";
    private static final PasswordEncoder BCRYPT = new BCryptPasswordEncoder();

    @Override
    public String encode(CharSequence rawPassword) {

    return BCRYPT.encode(rawPassword);
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {

    if (encodedPassword.startsWith(BCRYP_TYPE)) {
        return BCRYPT.matches(rawPassword, encodedPassword);
    }

    return sha1SaltMatch(rawPassword, encodedPassword);
    }

    @SneakyThrows
    private boolean sha1SaltMatch(CharSequence rawPassword, String encodedPassword) {

    String[] saltHash = encodedPassword.split(User.SPLIT_CHAR);

    // Legacy code from old system   
    byte[] b64salt = Base64.getDecoder().decode(saltHash[0].getBytes());
    byte[] validHash = Base64.getDecoder().decode(saltHash[1]);
    byte[] checkHash = Utility.getHash(5, rawPassword.toString(), b64salt);

    return Arrays.equals(checkHash, validHash);
    }

}

用户对象

public class User implements UserDetails {

    public static final String SPLIT_CHAR = ":";

    @Id
    @Column(name = "user_id", nullable = false)
    private Integer userId;

    @Column(nullable = false, length = 60)
    private String password;

    @Column(nullable = true, length = 32)
    private String salt;

.
.

    @PostLoad
    private void init() {

    username = emailAddress; //To comply with UserDetails
    password = salt == null ? password : salt + SPLIT_CHAR + password;
    }        

您还可以添加一个挂钩,以新的BCrypt格式重新编码密码并替换它。从而逐步淘汰旧方法。

于 2016-01-08T15:05:46.493 回答
3

刚刚在互联网上阅读了这个和 Spring 中的选项,我会第二个 Luke 的答案,使用 BCrypt(它在 Spring 的源代码中提到)。

我发现解释为什么要散列/加盐以及为什么使用 BCrypt 是一个不错的选择的最佳资源是:Salted Password Hashing - Doing it Right

于 2013-07-03T14:24:08.993 回答