将 Spring 安全性 4.x(ShaPasswordEncoder 和每个用户的唯一盐)迁移到 5.x(BCryptPasswordEncoder)

Migrate Spring Security 4.x (ShaPasswordEncoder and unique salt for each user) to 5.x (BCryptPasswordEncoder)

我正在将 Spring 安全性从 4.2.16.RELEASE 版本迁移到 5.6.3 版本。 现有的项目安全配置使用已弃用的 ShaPasswordEncoder 和自定义的 SaltSource 生成盐。 salt 由应用程序配置 属性 和用户的 uuid:

组成
     <bean id="customAuthenticationProvider" class="com.my.security.CustomUserDetailsAuthenticationProvider">
        <property name="userDetailsService" ref="CustomUserDetailsService" />
        <property name="saltSource" ref="saltSource" />
        <property name="passwordEncoder" ref="passwordEncoder" />
        <property name="servicesURL" value="${custom.services.url}" />
    </bean>
    <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
        <constructor-arg value="256" />
    </bean>
    <bean id="saltSource" class="com.my.security.UserSaltSource">
        <property name="userPropertyToUse" value="CustomId" />
        <property name="systemWideSalt" value="${security.systemwide.salt}" />
    </bean>

getSalt方法:

public Object getSalt(String secondSaltProperty) {
    try {       
        return secondSaltProperty + this.systemWideSalt;
    } catch (Exception exception) {
        throw new AuthenticationServiceException(exception.getMessage(),
                exception);
    }
}

我的目标不仅是迁移到较新的 Spring 安全版本,而且还开始使用推荐的 BCryptPasswordEncoder 密码编码器。 根据migration guide,我必须迁移现有密码:

The format is intended to work with the DigestPasswordEncoder that was found in the Spring Security core module. However, the passwords will need to be migrated to include any salt with the password since this API provides Salt internally vs making it the responsibility of the user. To migrate passwords from the SaltSource use the following: String salt = saltSource.getSalt(user); String s = salt == null ? null : "{" + salt + "}"; String migratedPassword = s + user.getPassword();

问题: 密码迁移部分我不清楚的是结合了新的密码存储格式{id}encodedPassword,其中使用了id用于选择相应的密码编码器和盐部分。 根据 migration guide 我必须在存储的密码中添加 {sha256} 前缀,因此 DelegatingPasswordEncoder 将选择正确的解码器,但是如何处理 salt 部分呢?

我的理解如下: 首先,我必须定义 DelegatingPasswordEncoder,如果我不在密码中添加 id 前缀,我必须使用 DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches 方法设置默认密码编码器以获取旧密码编码器. 接下来,在自定义AuthenticationProvider服务class中实现认证逻辑,其中注入passwordEncoder用于密码匹配校验。在调用 PasswordEncoder.matches(CharSequence rawPassword, String encodedPassword); 之前,我必须添加将格式化编码密码以匹配 {salt}password 格式的代码。

下一步是添加成功的身份验证侦听器,它将重新编码密码以匹配 {bcrypt}password 格式并将其存储到数据库中。因此,在下次用户登录尝试时,将改为调用 BCryptPasswordEncoder 密码编码器。

迁移所有密码后,我将能够删除自定义的 SaltSource-wise 内容和成功的身份验证侦听器。

如果我错了或者遗漏了什么,请纠正我。谢谢。

您只需要做两件事。

  1. 迁移密码以在模式中包含盐 {salt}<password-hash>
  2. 配置 DelegatingPasswordEncoder 并将正确配置的 MessageDigestPasswordEncoder 设置为默认值。因此,如果未指定编码,它将回退到那个编码。

如果您想即时转换密码,您可以使用成功处理程序并更新记录以使用 BCrypt/SCrypt 而不是您现在拥有的内容。