【问题标题】:Spring Security - user authentication with BCrypt hashed password (bad credentials error)Spring Security - 使用 BCrypt 哈希密码进行用户身份验证(错误凭据错误)
【发布时间】:2017-04-25 15:28:06
【问题描述】:

注册新用户后,数据库中生成的哈希密码与用户输入的密码不匹配,以便进行身份验证。原始密码相同,但散列版本不同。我想知道如何让这两者相互匹配以进行正确的身份验证?我使用 Spring 4.3.2.RELEASE 和 4.2.0.RELEASE 来保证安全性。

我还有一个警告:

WARN SpringSecurityCoreVersion:78 - **** You are advised to use Spring 4.3.4.RELEASE or later with this version. You are running: 4.3.0.RELEASE

也许这会导致某种问题。

config.xml:

<bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

<security:authentication-manager>
    <security:authentication-provider user-service-ref="userService">
        <security:password-encoder ref="encoder"/>
    </security:authentication-provider>
</security:authentication-manager>

<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
    <property name="userDetailsService" ref="userService" />
    <property name="hideUserNotFoundExceptions" value="false" />
    <property name="passwordEncoder" ref="encoder" />
</bean>

<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
    <constructor-arg>
        <ref bean="daoAuthenticationProvider" />
    </constructor-arg>
</bean>

UserEntity.java:

public void setPassword(String password) {
    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    this.password = passwordEncoder.encode(password);
}

UserAuthenticationProviderService.java:

public boolean processUserAuthentication(UserEntity user) {

    try {
        Authentication request = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        Authentication result = authenticationManager.authenticate(request);
        SecurityContextHolder.getContext().setAuthentication(result);

        return true;
    } catch(AuthenticationException e) {
        FacesContext.getCurrentInstance().addMessage(null, 
                new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), "Catched Error!"));

        return false;
    }
}

编辑:已解决。

正如 Shaun 所说,问题在于实体类中的编码。将编码移动到用户创建位置后一切正常,因为编码现在只出现在用户创建过程中。谢谢!

【问题讨论】:

  • 您将名称空间配置和普通 Java bean 用于身份验证管理器。您应该选择其中之一。另外,你的问题不是很清楚。 “散列版本不同”是什么意思 - 你怎么知道?你在比较什么,在哪里比较?数据库中的值是否可识别为 bcrypt 哈希?发布一个示例并使用实际(测试)数据说明您的意思。
  • 用密码注册后:“superman”,密码在数据库中保存为:“$2a$10$lwRt//HRsibkzHQzECHxi.E2hacnQve3JTZkxlrwlvVZ1S0w1DKO6”就可以了。但在那之后,当应用程序尝试自动登录时,数据库中的密码更改为:“$2a$10$19Z.a2KhDELFMJ55VhxQtOvaZsO0n8q2VLXUqBpxsmHfj3j3NkQjq”并保持这种状态。看起来像是在同一次运行中用错误的密码更新了正确的密码,但现在我不知道原因。
  • 更新:不会一直这样。在任何尝试登录后它正在更新(更新仍然不匹配)
  • Spring Security 不会更新您的数据库 - 您必须在其他地方进行更新。您应该调试这些更改并找出它们来自哪里以及正在散列的值是什么。实体代码令人困惑。如果那是数据库实体,为什么要使用其中的密码进行身份验证?看起来您正在获取一个已经散列的密码并将其传递给 AuthenticationManager,它将在检查期间再次散列它(这显然会失败)。

标签: java spring spring-mvc authentication spring-security


【解决方案1】:

是的,您注意到在 BCryptEncoder 对同一字符串进行 2 次编码后,您将得到不同的字符串。但是 Spring Security 不使用等于的匹配。当你注册你的编码器时,SPring Security 会使用 PasswordEncoder 中的boolean matches(CharSequence rawPassword, String encodedPassword)(BCryptEncoder 实现了这个接口)。

如果您对细节感兴趣,可以查看 BCrypt 的实现,非常简单:

static boolean equalsNoEarlyReturn(String a, String b) {
        char[] caa = a.toCharArray();
        char[] cab = b.toCharArray();

        if (caa.length != cab.length) {
            return false;
        }

        byte ret = 0;
        for (int i = 0; i < caa.length; i++) {
            ret |= caa[i] ^ cab[i];
        }
        return ret == 0;
    }

【讨论】:

  • 感谢您的回答,但我仍然不明白为什么 Spring Security 不使用 match() 函数匹配这些密码,而编码器在安全配置文件中被定义为 BCrypt。 authenticationManager.authenticate() 是否有可能无法识别密码经过 BCrypt 哈希处理并将其作为原始密码进行比较?
  • @rchtp 当您通过 auth.userDetailsS​​ervice(userDetailsS​​ervice).passwordEncoder(new BCryptPasswordEncoder()); 明确表示时,它确实将其识别为 BCrypt;当我切换到 BCrypt 时,我不需要改变任何东西。
猜你喜欢
  • 2016-06-06
  • 2015-02-28
  • 2016-08-27
  • 2014-09-25
  • 1970-01-01
  • 1970-01-01
  • 2011-03-04
  • 2014-02-18
  • 2020-08-17
相关资源
最近更新 更多