You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用bcrypt加密时,如何更新用户信息而不修改数据库密码?

解决AngularJS + Spring更新用户详情时的密码重复加密问题

Hey, I've run into this exact issue before with Spring and AngularJS—let's break down what's going wrong and how to fix it:

Your core problem is two-fold:

  1. Your setPassword() method encrypts every value passed to it, even if it's already the encrypted password from your database. That's why existing passwords get re-encrypted when you update other user details.
  2. You're forced to pass the password every time, even when the user isn't changing it.

Here are three solid solutions, ordered by how clean they are for your use case:

The biggest issue right now is that your entity's setter is handling encryption, which mixes domain logic with data persistence. Let's fix that:

Step 1: Update the User entity's password setter

Make it a simple setter without encryption:

public String getPassword() { 
    return password; 
} 

public void setPassword(String password) { 
    // No encryption here—we'll handle that in the service
    this.password = password; 
}

Step 2: Handle encryption in your UserService

Only encrypt the password when creating a new user or when the user explicitly updates their password:

@Service
public class UserService {
    private final UserRepository userRepository;
    private final BCryptPasswordEncoder passwordEncoder;

    // Constructor injection (preferred over @Autowired)
    public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }

    // For creating new users
    public User createUser(User newUser) {
        newUser.setPassword(passwordEncoder.encode(newUser.getPassword()));
        return userRepository.save(newUser);
    }

    // For updating user details
    public User updateUser(User updatedUser) {
        // Fetch the existing user from the database
        User existingUser = userRepository.findById(updatedUser.getId())
            .orElseThrow(() -> new RuntimeException("User not found with ID: " + updatedUser.getId()));

        // Update all non-password fields
        existingUser.setFirstName(updatedUser.getFirstName());
        existingUser.setMiddleName(updatedUser.getMiddleName());
        existingUser.setLastName(updatedUser.getLastName());
        existingUser.setEmailAddress(updatedUser.getEmailAddress());
        existingUser.setBday(updatedUser.getBday());
        existingUser.setContactNo(updatedUser.getContactNo());
        existingUser.setAddress(updatedUser.getAddress());
        existingUser.setGender(updatedUser.getGender());
        existingUser.setUsername(updatedUser.getUsername());
        existingUser.setRole(updatedUser.getRole());

        // Only update password if a new one was provided (and it's not empty)
        if (updatedUser.getPassword() != null && !updatedUser.getPassword().trim().isEmpty()) {
            existingUser.setPassword(passwordEncoder.encode(updatedUser.getPassword()));
        }

        return userRepository.save(existingUser);
    }
}

Step 3: Update your AngularJS code to only send password when needed

Don't include the password in the payload unless the user is changing it. Add a password input field for edits, and only attach it if the user entered something:

var profile = {
    "id": $scope.userData.id,
    "firstName": $scope.first,
    "middleName": $scope.mid,
    "lastName": $scope.last,
    "emailAddress": $scope.mail,
    "bday": $scope.bday,
    "contactNo": $scope.num,
    "address": $scope.add,
    "gender": $scope.gender,
    "username": $scope.userData.username,
    "role": $scope.userData.role
};

// Only add password to the payload if user entered a new one
if ($scope.newPassword && $scope.newPassword.trim() !== '') {
    profile.password = $scope.newPassword;
}

// Send the profile object to your Spring backend via $http or $resource

2. Modify the setter to skip encryption for existing BCrypt passwords

If you don't want to move the encryption logic right now, you can add a check in the setter to detect if the input is already a BCrypt-encrypted string (they start with $2a$, $2b$, or $2y$):

public void setPassword(String password) {
    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    // Check if the password is already encrypted
    if (password != null && (password.startsWith("$2a$") || password.startsWith("$2b$") || password.startsWith("$2y$"))) {
        this.password = password;
    } else {
        // Encrypt only if it's a plaintext password
        this.password = passwordEncoder.encode(password);
    }
}

Note: This works, but it's not the cleanest approach because it still ties encryption logic to the entity class.

3. Use partial updates with PATCH

If you're using Spring MVC, you can use @PatchMapping and update only the fields that were sent in the request. Combine this with a tool like ModelMapper to copy non-null fields from the request DTO to the existing entity:

@PatchMapping("/users/{id}")
public ResponseEntity<User> updateUserPartial(@PathVariable Long id, @RequestBody UserUpdateDto updateDto) {
    User existingUser = userRepository.findById(id)
        .orElseThrow(() -> new RuntimeException("User not found"));

    // Use ModelMapper to copy only non-null fields
    ModelMapper modelMapper = new ModelMapper();
    modelMapper.getConfiguration().setSkipNullEnabled(true);
    modelMapper.map(updateDto, existingUser);

    // Handle password encryption if needed
    if (updateDto.getPassword() != null && !updateDto.getPassword().trim().isEmpty()) {
        existingUser.setPassword(passwordEncoder.encode(updateDto.getPassword()));
    }

    return ResponseEntity.ok(userRepository.save(existingUser));
}

This is great for reducing payload size and keeping updates focused, but it requires creating a DTO class for partial updates.


Any of these approaches will let you update user details without re-encrypting the existing password or forcing users to re-enter their password every time. My top pick is the first one—it keeps your code organized and follows best practices for layered architecture.

内容的提问来源于stack exchange,提问作者Mark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:35:06