【问题标题】:How to use new PasswordEncoder from Spring Security如何使用 Spring Security 的新 PasswordEncoder
【发布时间】:2013-06-30 22:08:44
【问题描述】:

从 Spring Security 3.1.4.RELEASE 开始,旧的 org.springframework.security.authentication.encoding.PasswordEncoder has been deprecated 支持 org.springframework.security.crypto.password.PasswordEncoder。由于我的应用程序尚未向公众发布,我决定迁移到新的、未弃用的 API。

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

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

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

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

【问题讨论】:

    标签: java spring spring-security


    【解决方案1】:

    我遇到了类似的问题。我需要保留旧的加密密码(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 格式重新编码密码并替换它。从而逐步淘汰旧方法。

    【讨论】:

      【解决方案2】:

      这是对我有用的 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例子Click Here

      希望这会有所帮助。

      谢谢

      【讨论】:

      • 您的 userDetailsS​​erviceImpl 中包含什么?
      【解决方案3】:

      刚刚在互联网上阅读了这一点以及 Spring 中的选项,我将第二个 Luke 的答案,使用 BCrypt(它在 Spring 的 source code 中提到)。

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

      【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 如果您确实有注册用户怎么办?我假设 Passwordencoder 将在某个时候被删除。如何迁移?
      • 迁移帐户通常需要您在用户成功登录时重新散列密码。您还必须在迁移期间支持多种算法。除此之外,您可能需要重置密码或最终锁定或删除未使用一段时间的未使用帐户。这取决于您的系统和要求。我敢肯定,如果您进行一些搜索,您可以找到有关它的讨论,因为这是一个常见问题,并且随着密码数据库泄露数量的增加而变得更加相关。至少你没有使用plaintext :-)。
      • 我有一个正在运行的应用程序,并且正在使用旧的 PasswordEncoder 和 salt。有没有关于如何迁移到新 PasswordEncoder 的示例?
      • 在代码方面并不太复杂 - 例如,请参阅 this answer。它更多地是关于所涉及的过程,基于诸如您的用户登录频率以及您希望迁移过程需要多长时间,可能要求用户在一段时间不活动后更改密码或锁定帐户之类的事情。跨度>
      猜你喜欢
      • 2014-05-26
      • 2016-11-28
      • 2021-08-30
      • 1970-01-01
      • 2013-07-02
      • 2015-12-06
      • 1970-01-01
      • 2015-01-07
      • 1970-01-01
      相关资源
      最近更新 更多