有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何使用Spring“匹配”方法检查旧密码?

@Autowired
private PasswordEncoder passwordEncoder;

@Autowired
private OldPasswordsService oldPasswordsService;

Optional<OldPasswords> list = oldPasswordsService.findEncryptedPassword(passwordEncoder.encode("new password entered form web reset form"));
            OldPasswords value = list.get();
            boolean matches = passwordEncoder.matches("new password entered form web reset form", value.getEncryptedPassword());

            if (matches)
            {
                return new ResponseEntity<>("PASSWORD_ALREADY_USED", HttpStatus.BAD_REQUEST);
            }
            else
            {
                OldPasswords oldPasswords = new OldPasswords();
                oldPasswords.setEncryptedPassword(passwordEncoder.encode(resetDTO.getPassword()));
                oldPasswordsService.save(oldPasswords);
            }

旧密码表:

@Table(name = "old_passwords")
public class OldPasswords implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, updatable = false, nullable = false)
    private int id;

    @Column(name = "encrypted_password", length = 255)
    private String encryptedPassword;

    @Column(name = "password_owner_id", length = 4)
    private Integer passwordOwnerId;

    @Column(name = "created_at")
    @Convert(converter = LocalDateTimeConverter.class)
    private LocalDateTime createdAt;

但我明白了。 你知道我如何实现一个比较新旧密码的逻辑吗

编辑: 我试过这个设计:

SQL查询:

public List<OldPasswords> findByOwnerId(Integer ownerId) {
        String hql = "select e from " + OldPasswords.class.getName() + " e where e.passwordOwnerId = :passwordOwnerId ORDER BY e.createdAt DESC";
        TypedQuery<OldPasswords> query = entityManager.createQuery(hql, OldPasswords.class).setMaxResults(3).setParameter("passwordOwnerId", ownerId);
        List<OldPasswords> list = query.getResultList();
        return list;
    }

终点:

@PostMapping("reset_password")
  public ResponseEntity<?> reset(@RequestBody PasswordResetDTO resetDTO) {
    return this.userService.findByLogin(resetDTO.getName()).map(user -> {

        Integer userId = user.getId();

        List<OldPasswords> list = oldPasswordsService.findByOwnerId(userId);

        if(!list.isEmpty() && !list.isEmpty()) {

            for (int i = 0; i<list.size(); i++){
                OldPasswords value = list.get(i);

                boolean matches = passwordEncoder.matches(resetDTO.getPassword(), value.getEncryptedPassword());
                if (matches) {
                    return new ResponseEntity<>("PASSWORD_ALREADY_USED", HttpStatus.BAD_REQUEST);
                }
            }
        }

        OldPasswords oldPasswords = new OldPasswords();
        oldPasswords.setEncryptedPassword(passwordEncoder.encode(resetDTO.getPassword()));
        oldPasswords.setPasswordOwnerId(userId);
        oldPasswordsService.save(oldPasswords);

        user.setEncryptedPassword(passwordEncoder.encode(resetDTO.getPassword()));

        user.setResetPasswordToken(null);
        userService.save(user);
        return ok().build();
    }).orElseGet(() -> notFound().build());
}

但是,当我使用相同的密码多次更改代码时,错误PASSWORD_ALREADY_USED不会显示


共 (3) 个答案

  1. # 1 楼答案

    我用一个简单的解决方案解决了这个问题

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
        if (encoder.matches(newPassword, oldPassword)) 
        {
              System.out.println("Successfully logged in!");
    
        }
          else 
               {
                    System.out.println("Incorrect Password.");
                }
    

    newPasswordoldPassword必须在字符串中,以便它匹配两个密码

  2. # 2 楼答案

    检查可选类型列表是否包含有效的必需值

    Optional<OldPasswords> list = oldPasswordsService.findEncryptedPassword(passwordEncoder.encode("new password entered form web reset form"));
    OldPasswords value = list.orElse(null);
    
    if(value != null) {
        boolean matches = passwordEncoder.matches("new password entered form web reset form", value.getEncryptedPassword());
        if (matches) {
            return new ResponseEntity<>("PASSWORD_ALREADY_USED", HttpStatus.BAD_REQUEST);
        }
    
        OldPasswords oldPasswords = new OldPasswords();
        oldPasswords.setEncryptedPassword(passwordEncoder.encode(resetDTO.getPassword()));
        oldPasswordsService.save(oldPasswords);
        return new ResponseEntity<>("New Password Saved" , HttpStatus.OK);
    }
    return new ResponseEntity<>("Incorrect Password Provided" , HttpStatus.BAD_REQUEST);
    

    或者像@Manuel建议的那样

    Optional<OldPasswords> list = oldPasswordsService.findEncryptedPassword(passwordEncoder.encode("new password entered form web reset form"));
    
    if(list.isPresent()) {
        OldPasswords value = list.get();
        boolean matches = passwordEncoder.matches("new password entered form web reset form", value.getEncryptedPassword());
        if (matches) {
            return new ResponseEntity<>("PASSWORD_ALREADY_USED", HttpStatus.BAD_REQUEST);
        }
    
        OldPasswords oldPasswords = new OldPasswords();
        oldPasswords.setEncryptedPassword(passwordEncoder.encode(resetDTO.getPassword()));
        oldPasswordsService.save(oldPasswords);
        return new ResponseEntity<>("New Password Saved" , HttpStatus.OK);
    }
    return new ResponseEntity<>("Incorrect Password Provided" , HttpStatus.BAD_REQUEST);
    
  3. # 3 楼答案

    我认为你的代码有几个问题

    一,。“passwordEncoder”的类型

    根据实际使用的编码算法,有不同类型的密码编码器。 如果“passwordEncoder”的类型为MD5、SHA1,则很可能会发生密码冲突,因为您希望密码是唯一的

    意味着如果一个用户有一个弱密码,例如“topSecret123”,而另一个用户有相同的密码“topSecret123”,您可以使用该方法

    oldPasswordsService.findEncryptedPassword(...)
    

    将返回多个条目,而不是一个

    这将导致NonUniqueResultException或其他事情

    1.1可能的解决方案:

    将密码与用户名关联。获取用户ID(或类似信息)提供的用户,并使用该用户的密码进行密码检查

    1.2另一种可能的解决方案

    使用一个例如BCryptPasswordEncoder。这种类型的PasswordEncoder负责向你的has中添加盐。这避免了数据库中可能存在的重复条目。 如果只提供“密码”,这些类型的密码编码器无法计算密码或检查密码是否匹配。由于这些类型的密码编码器将“salt”与您的编码密码一起使用,因此需要(salt+散列)密码作为输入,以检查提供的密码是否匹配

    二,。实际问题

    密码

    OldPasswords value = list.get();
    

    这就是问题所在。Optional<OldPasswords>可以包含null值。 对Optional值的.get()调用null将导致java.util.NoSuchElementException: No value present

    Optional<OldPasswords> list = oldPasswordsService.findEncryptedPassword(passwordEncoder.encode("new password entered form web reset form"));
    
    if (!list.isPresent()) {
     return new ResponseEntity<>("The old password value you've entered is incorrect", HttpStatus.UNAUTHORIZED);
    }
    
    OldPasswords value = list.get();
    boolean matches = passwordEncoder.matches("new password entered form web reset form", value.getEncryptedPassword());
    
    if (matches)
    {
        return new ResponseEntity<>("PASSWORD_ALREADY_USED", HttpStatus.BAD_REQUEST);
    }
    else
    {
        OldPasswords oldPasswords = new OldPasswords();
        oldPasswords.setEncryptedPassword(passwordEncoder.encode(resetDTO.getPassword()));
        oldPasswordsService.save(oldPasswords);
    }
    

    三,。旧密码实体

    您既不必使@Idunique=true,也不必使nullable=falseupdateable=false

    四,。混合层

    您发布的代码使用服务并更新域对象。 它确实返回ResponseEntity。显然,您可以将应用程序的不同层混合到一个应用程序中

    五,。公开信息

    您公开了所选(新)密码已被其他用户使用的信息!别那么做!这是因为第一点。我已经列出了

    编辑:

    问题更新后,我也想更新我的答案。 由于更新后的问题中截取的代码无法编译,我想根据我从代码片段中了解到的内容,制作一个非常简单、基本的示例

    我不评论问题中显示的“重置密码”设计的概念,因为在这两者之间缺少很多代码

    包括测试在内的整个代码可以在这里找到:https://github.com/mschallar/so_oldpasswords_example

    问题中要求的功能代码为:

    @PostMapping("reset_password")
    public ResponseEntity<?> reset(@RequestBody PasswordResetDTO resetDTO) {
    
        Optional<User> findByLogin = this.userService.findByLogin(resetDTO.getName());
    
        if (!findByLogin.isPresent()) {
            return ResponseEntity.notFound().build();
        }
    
        User user = findByLogin.get();
        Integer userId = user.getUserId();
    
        String encodedPassword = passwordEncoder.encode(resetDTO.getPassword());
    
        for (OldPasswords oldPasswords : oldPasswordsService.findByOwnerId(userId)) {
    
            if (passwordEncoder.matches(resetDTO.getPassword(), oldPasswords.getEncryptedPassword())) {
                // Information: Don't do that! Don't reveal that another user already has such a password!
                log.info("Password already used.");
                return new ResponseEntity<>("PASSWORD_ALREADY_USED", HttpStatus.BAD_REQUEST);
            }
    
        }
    
        OldPasswords oldPasswords = new OldPasswords();
        oldPasswords.setEncryptedPassword(passwordEncoder.encode(encodedPassword));
        oldPasswords.setPasswordOwnerId(userId);
        oldPasswordsService.save(oldPasswords);
    
        user.setEncryptedPassword(encodedPassword);
    
        user.setResetPasswordToken(null);
        userService.save(user);
    
        return ResponseEntity.ok().build();
    
    }