需求:在更新数据时,如果用户没有权限修改某个字段,则保留原数据的字段值;如果有权限,则更新为新数据。以下是实现方案:
整体流程
- 查询原始数据:从数据库中获取原始实体对象。
- 检查字段权限:
- 遍历需要更新的字段。
- 如果用户没有权限修改某个字段,则保留原始数据的字段值。
- 如果用户有权限,则更新为新数据的字段值。
- 执行业务逻辑:将处理后的数据传递给Service层,执行后续的业务逻辑。
- 更新数据:将最终的数据更新到数据库中。
代码实现
1. 定义字段权限注解
用于标记哪些字段需要权限控制。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataPermission {
String[] roles() default {}; // 允许的角色
String[] permissions() default {}; // 允许的权限(如 WRITE)
}
2. 在实体类中标记字段权限
在实体类中使用注解标记需要权限控制的字段。
public class User {
@DataPermission(roles = {"ADMIN"}, permissions = {"WRITE"})
private String username;
@DataPermission(roles = {"ADMIN", "MANAGER"}, permissions = {"WRITE"})
private String email;
// 其他字段
}
3. 权限检查与字段更新逻辑
编写一个工具类,用于检查字段权限并更新数据。
public class DataPermissionUtil {
/**
* 检查字段权限并更新数据
*
* @param original 原始数据
* @param updated 更新数据
* @param userRole 用户角色
* @param userPermissions 用户权限集合
* @param <T> 实体类型
* @return 处理后的实体
*/
public static <T> T checkAndUpdateFields(T original, T updated, String userRole, Set<String> userPermissions) {
Class<?> clazz = original.getClass();
try {
for (Field field : clazz.getDeclaredFields()) {
DataPermission annotation = field.getAnnotation(DataPermission.class);
if (annotation != null) {
boolean hasRole = Arrays.asList(annotation.roles()).contains(userRole);
boolean hasPermission = Arrays.stream(annotation.permissions())
.anyMatch(userPermissions::contains);
field.setAccessible(true);
Object updatedValue = field.get(updated);
// 如果用户没有权限修改该字段,则保留原始值
if (!hasRole || !hasPermission) {
field.set(updated, field.get(original));
} else if (updatedValue != null) {
// 如果有权限且新值不为空,则更新为新值
field.set(updated, updatedValue);
}
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to update fields due to permission check", e);
}
return updated;
}
}
4. 在Service层调用权限检查逻辑
在Service层中,调用权限检查工具类,确保数据更新符合权限要求。
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private UserContext userContext; // 获取当前用户信息
public void updateUser(User updatedUser) {
// 1. 查询原始数据
User originalUser = userRepository.findById(updatedUser.getId());
// 2. 检查字段权限并更新数据
User finalUser = DataPermissionUtil.checkAndUpdateFields(
originalUser,
updatedUser,
userContext.getCurrentUserRole(),
userContext.getCurrentUserPermissions()
);
// 3. 执行业务逻辑(如果有)
// 例如:验证数据、触发事件等
// 4. 更新数据到数据库
userRepository.update(finalUser);
}
}
5. 集成到Controller层
在Controller层接收前端传入的更新数据,并调用Service层的方法。
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PutMapping("/{id}")
public ResponseEntity<String> updateUser(@PathVariable Long id, @RequestBody User updatedUser) {
updatedUser.setId(id); // 确保ID一致
userService.updateUser(updatedUser);
return ResponseEntity.ok("User updated successfully");
}
}
流程总结
- 前端传入更新数据:通过HTTP请求传入需要更新的字段。
- 查询原始数据:从数据库中获取原始实体对象。
- 权限检查与字段更新:
- 遍历字段,检查用户是否有权限修改。
- 如果没有权限,则保留原始值;如果有权限,则更新为新值。
- 执行业务逻辑:在Service层中执行额外的业务逻辑(如数据验证、触发事件等)。
- 更新数据库:将处理后的数据保存到数据库中。
示例场景
假设:
- 原始数据:
{ "username": "admin", "email": "admin@example.com" } - 更新数据:
{ "username": "new_admin", "email": "new_admin@example.com" } - 用户角色:
MANAGER - 权限配置:
username:仅ADMIN可写。email:ADMIN和MANAGER可写。
处理结果:
username:用户没有权限修改,保留原始值"admin"。email:用户有权限修改,更新为"new_admin@example.com"。
最终更新到数据库的数据为:{ "username": "admin", "email": "new_admin@example.com" }。
优化建议
- 缓存原始数据:如果查询原始数据的开销较大,可以考虑缓存原始数据。
- 批量更新支持:如果需要支持批量更新,可以扩展工具类以处理多个实体。
- 日志记录:记录权限检查的日志,便于审计和排查问题。
- 单元测试:编写单元测试,覆盖各种权限场景。
希望这个方案能够满足你的需求!如果有进一步的问题,欢迎随时讨论。