1. 业务逻辑
修改密码包括两种情况:
- 用户修改自己的密码:
- 需要登录后才能修改(安全考虑)
- 需要提供旧密码(验证身份)
- 需要提供新密码
- 管理员重置用户密码:
- 不需要旧密码
- 直接设置新密码
- 需要管理员权限
2. 后端代码
1. 创建密码修改的DTO
ChangePasswordRequest.java
package com.quickstore.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Data
public class ChangePasswordRequest {
@NotBlank(message = "旧密码不能为空")
private String oldPassword;
@NotBlank(message = "新密码不能为空")
@Size(min = 6, max = 40, message = "新密码长度必须在6-40之间")
private String newPassword;
}
ResetPasswordRequest.java
package com.quickstore.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Data
public class ResetPasswordRequest {
@NotBlank(message = "新密码不能为空")
@Size(min = 6, max = 40, message = "新密码长度必须在6-40之间")
private String newPassword;
}
2. 在UserService中添加密码修改方法
package com.quickstore.service;
import com.quickstore.model.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import java.util.List;
public interface UserService extends UserDetailsService {
User findByUsername(String username);
User save(User user);
List<User> findAllUsers();
User findById(Long id);
void deleteUser(Long id);
void changePassword(String username, String oldPassword, String newPassword);
void resetPassword(Long userId, String newPassword);
}
3. 在UserServiceImpl中实现这些方法
package com.quickstore.service.impl;
import com.quickstore.model.User;
import com.quickstore.repository.UserRepository;
import com.quickstore.service.UserService;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.security.authentication.BadCredentialsException;
import java.util.Collections;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public UserServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPasswordHash(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + user.getRole().toUpperCase()))
);
}
@Override
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
@Override
public User save(User user) {
if (user.getRole() != null) {
user.setRole(user.getRole().toLowerCase());
}
return userRepository.save(user);
}
@Override
public List<User> findAllUsers() {
return userRepository.findAll();
}
@Override
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
@Override
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
@Override
public void changePassword(String username, String oldPassword, String newPassword) {
User user = findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found with username: " + username);
}
if (!passwordEncoder.matches(oldPassword, user.getPasswordHash())) {
throw new BadCredentialsException("Invalid old password");
}
user.setPasswordHash(passwordEncoder.encode(newPassword));
userRepository.save(user);
}
@Override
public void resetPassword(Long userId, String newPassword) {
User user = findById(userId);
if (user == null) {
throw new UsernameNotFoundException("User not found with id: " + userId);
}
user.setPasswordHash(passwordEncoder.encode(newPassword));
userRepository.save(user);
}
}
4. 在UserController中添加密码修改端点
package com.quickstore.controller;
import com.quickstore.dto.ChangePasswordRequest;
import com.quickstore.dto.ResetPasswordRequest;
import com.quickstore.dto.UserUpdateRequest;
import com.quickstore.model.User;
import com.quickstore.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<List<User>> getAllUsers() {
logger.info("Attempting to get all users");
List<User> users = userService.findAllUsers();
logger.info("Found {} users", users.size());
return ResponseEntity.ok(users);
}
@PutMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> updateUser(@PathVariable Long id, @RequestBody UserUpdateRequest request) {
logger.info("Attempting to update user with id: {}", id);
User user = userService.findById(id);
if (user == null) {
logger.warn("User not found with id: {}", id);
return ResponseEntity.notFound().build();
}
// 更新用户信息
user.setFullName(request.getFullName());
user.setRole(request.getRole().toLowerCase());
// 保存更新
userService.save(user);
logger.info("User updated successfully: {}", user.getUsername());
return ResponseEntity.ok(user);
}
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> deleteUser(@PathVariable Long id) {
logger.info("Attempting to delete user with id: {}", id);
User user = userService.findById(id);
if (user == null) {
logger.warn("User not found with id: {}", id);
return ResponseEntity.notFound().build();
}
userService.deleteUser(id);
logger.info("User deleted successfully: {}", user.getUsername());
return ResponseEntity.ok().build();
}
@PostMapping("/change-password")
public ResponseEntity<?> changePassword(Authentication authentication, @RequestBody ChangePasswordRequest request) {
String username = authentication.getName();
logger.info("Attempting to change password for user: {}", username);
try {
userService.changePassword(username, request.getOldPassword(), request.getNewPassword());
logger.info("Password changed successfully for user: {}", username);
return ResponseEntity.ok().build();
} catch (Exception e) {
logger.error("Failed to change password for user: {}", username, e);
return ResponseEntity.badRequest().body(e.getMessage());
}
}
@PostMapping("/{id}/reset-password")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> resetPassword(@PathVariable Long id, @RequestBody ResetPasswordRequest request) {
logger.info("Attempting to reset password for user id: {}", id);
try {
userService.resetPassword(id, request.getNewPassword());
logger.info("Password reset successfully for user id: {}", id);
return ResponseEntity.ok().build();
} catch (Exception e) {
logger.error("Failed to reset password for user id: {}", id, e);
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
后端代码完成。
3. 用Postman进行测试
1. 用户修改自己的密码
1. 用Postman登录(获取token)
如上一篇所示。
2. 用Postman发送请求
- 发送 POST 请求到 http://localhost:8080/api/users/change-password
- 请求头:
Authorization: Bearer {你的token}
Content-Type: application/json
- 请求体:
{
"oldPassword": "当前密码",
"newPassword": "新密码"
}
3. 运行结果
Header:
Body及结果:
成功!
验证修改后的密码:
登录成功!
2. 管理员重置用户密码
1. 用Postman登录(需是有admin权限的管理员)
同其他登录。
2. 用Postman发送请求
- 发送 POST 请求到 http://localhost:8080/api/users/{用户ID}/reset-password
- 请求头:
Authorization: Bearer {管理员token}
Content-Type: application/json
- 请求体:
{
"newPassword": "新密码"
}
3. 运行结果
Header:
Body及结果:
返回200状态,修改成功!
验证修改后的密码:
登录成功!
至此,密码修改的后端代码完成!
下一篇是密码修改功能的前端页面。