我正在使用Spring Boot在線創建一個Java的REST API,我想安全地將用戶密碼存儲在數據庫中, 爲此,我使用的BCrypt包含在Spring安全中,我使用MySQL和JPA- Hibernate的持久性。在春季開機時用春季安全密碼密碼的最佳做法是什麼?
而且我如下實現它:
這是用戶實體:
@Entity
@SelectBeforeUpdate
@DynamicUpdate
@Table (name = "USER")
public class User {
@Id
@GeneratedValue
@Column(name = "USER_ID")
private Long userId;
@Column(name = "ALIAS")
private String alias;
@Column(name = "NAME")
private String name;
@Column(name = "LAST_NAME")
private String lastName;
@Column(name = "TYPE")
private String type;
@Column(name = "PASSWORD")
private String password;
public String getPassword() {
return password;
}
/**
* When adding the password to the user class the setter asks if it is necessary or not to add the salt,
* if this is necessary the method uses the method BCrypt.hashpw (password, salt),
* if it is not necessary to add the salt the string That arrives is added intact
*/
public void setPassword(String password, boolean salt) {
if (salt) {
this.password = BCrypt.hashpw(password, BCrypt.gensalt());
} else {
this.password = password;
}
}
//Setters and Getters and etc.
}
這是用戶級的庫:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
這是服務用戶等級:
@Service
public class UserService{
private UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User addEntity(User user) {
//Here we tell the password setter to generate the salt
user.setPassword(user.getPassword(), true);
return userRepository.save(user);
}
public User updateEntity(User user) {
User oldUser = userRepository.findOne(user.getUserId());
/*
*This step is necessary to maintain the same password since if we do not do this
*in the database a null is generated in the password field,
*this happens since the JSON that arrives from the client application does not
*contain the password field, This is because to carry out the modification of
*the password a different procedure has to be performed
*/
user.setPassword(oldUser.getPassword(), false);
return userRepository.save(user);
}
/**
* By means of this method I verify if the password provided by the client application
* is the same as the password that is stored in the database which is already saved with the salt,
* returning a true or false boolean depending on the case
*/
public boolean isPassword(Object password, Long id) {
User user = userRepository.findOne(id);
//To not create an entity that only has a field that says password, I perform this mapping operation
String stringPassword = (String)((Map)password).get("password");
//This method generates boolean
return BCrypt.checkpw(stringPassword, user.getPassword());
}
/**
*This method is used to update the password in the database
*/
public boolean updatePassword(Object passwords, Long id) {
User user = userRepository.findOne(id);
//Here it receive a JSON with two parameters old password and new password, which are transformed into strings
String oldPassword = (String)((Map)passwords).get("oldPassword");
String newPassword = (String)((Map)passwords).get("newPassword");
if (BCrypt.checkpw(oldPassword, user.getPassword())){
//If the old password is the same as the one currently stored in the database then the new password is updated
//in the database for this a new salt is generated
user.setPassword(newPassword, true);
//We use the update method, passing the selected user
updateEntity(user);
//We return a true boolean
return true;
}else {
//If the old password check fails then we return a false boolean
return false;
}
}
//CRUD basic methods omitted because it has no case for the question
}
這是一個公開的API端點控制器:
@RestController
@CrossOrigin
@RequestMapping("/api/users")
public class UserController implements{
UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@RequestMapping(value = "", method = RequestMethod.POST)
public User addEntity(@RequestBody User user) {
return userService.addEntity(user);
}
@RequestMapping(value = "", method = RequestMethod.PUT)
public User updateEntity(@RequestBody User user) {
return userService.updateEntity(user);
}
@RequestMapping(value = "/{id}/checkPassword", method = RequestMethod.POST)
public boolean isPassword(@PathVariable(value="id") Long id, @RequestBody Object password) {
return userService.isPassword(password, id);
}
@RequestMapping(value = "/{id}/updatePassword", method = RequestMethod.POST)
public boolean updatePassword(@PathVariable(value="id") Long id, @RequestBody Object password) {
return userService.updatePassword(password, id);
}
}
這是我的問題來了,我的方法是工作,但我覺得這是不是最好的方式,我覺得不舒服更改密碼二傳手我寧願保留setter的標準形式,因爲在用戶服務中我認爲有機會處理用戶和密碼更新的不同,所以嘗試在實體中使用@DynamicUpdate
註釋,但它不能正常工作,因爲字段沒有在更新中提供,而不是像原先一樣保留它們。
我在尋找的是使用Spring Boot處理密碼安全性的更好方法。
你的回答讓有很大的意義,從這個想法來的MVC模型被跟隨,並且我從產生類似thymeleaf的意見,這種情況是訪問控制是由客戶端處理,爲的是變量'type'被使用,該API只需要適當地存儲在數據庫中的密碼和回答的問題,這是相同的密碼?並更新正確的,我的問題是有關的最佳方式中的數據來實現'PasswordEncoder'或一些其他的解決方案 – CorrOrtiz
在這種情況下,我會推薦給define'BCryptPasswordEncoder'豆和自動裝配'PasswordEncoder'接口(更容易使未來很可能切換到其他編碼器)以及它的方法'encode(..)'和'matches(...)'。一般來說,做同樣的事情爲你的代碼,但你不必在你的代碼上'BCrypt'嚴格依賴。我會將PasswordEncoder相關代碼放在一個具有自描述名稱的服務中,而不是放在setter中。此外,建議使用'字符串[]'或'的char []'與原始密碼操作(防止它們存儲在字符串常量池)。 – dimuha
如果你有時間把你解釋什麼一個小例子,這樣我可以表明它的答案,更新了回答這個問題 – CorrOrtiz