欢迎光临散文网 会员登陆 & 注册

把数据库里的未付款订单改成已付款,会发生什么

2023-07-17 00:00 作者:火星上的彩虹美不美  | 我要投稿

导言

不知道大家在网上购物的时候,有没有这样的念头,如果能把未付款的订单偷偷用一条SQL改成已付款,该多么美好啊。那么在实际开发过程中,我们应当如何保证数据库里的数据在保存后不会被偷偷更改?

大家好我是日暮与星辰之间,创作不易,如果觉得有用,求点赞,求收藏,求转发,谢谢。

理论

在介绍具体的内容之间,先介绍MD5算法,简单的来说,MD5能把任意大小、长度的数据转换成固定长度的一串字符,经常玩大型游戏的朋友应该都注意到过,各种补丁包、端游客户端之类的大型文件一般都附有一个MD5值,用于确保你下载文件的完整性。那么在这里,我们可以借鉴其思想,对订单的某些属性进行加密计算,得出来一个 MD5值一并保存在数据库当中。从数据库取出数据后第一时间进行校验,如果有异常更改,那么及时抛出异常进行人工处理。

实现

道理我都懂,但是我要如何做呢,别急,且听我一一道来。

这种需求听起来并不强绑定于某个具体的业务需求,这就要用到了我们熟悉的鼎鼎有名的AOP(面向切面编程)来实现。

首先定义四个类型的注解作为AOP的切入点。@Sign@Validate都是作用在方法层面的,分别用于对方法的入参进行加签和验证方法的返回值的签名。@SignField用于注解关键的不容篡改的字段。@ValidateField用于注解保存计算后得出的签名值。

less复制代码@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Sign { } less复制代码@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Validate { } less复制代码@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SignField { } less复制代码@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ValidField { }

以订单的实体为例 sn,amt,status,userId就是关键字段,绝不能允许有人在落单到数据库后对这些字段偷偷篡改。

typescript复制代码public class Order {     @SignField     private String sn;     @SignField     private String amt;     @SignField     private int status;     @SignField     private int userId;     @ValidField     private String sign; }

下面就到了重头戏的部分,如何通过AOP来进行实现。

1. 定义切入点

less复制代码@Pointcut("execution(@com.example.demo.annotations.Sign * *(..))") public void signPointCut() {  }  @Pointcut("execution(@com.example.demo.annotations.Validate * *(..))") public void validatePointCut() {  }

2.环绕切入点

ini复制代码@Around("signPointCut()") public Object signAround(ProceedingJoinPoint pjp) throws Throwable {     Object[] args = pjp.getArgs();     for (Object o : args) {         System.out.println(o);         sign(o);     }     Object res = pjp.proceed(args);     return res; }  @Around("validatePointCut()") public Object validateAround(ProceedingJoinPoint pjp) throws Throwable {     Object[] args = pjp.getArgs();     Object res = pjp.proceed(args);     valid(res);     return res; }

3. 签名的实现

1.获取需要签名字段

vbnet复制代码private Map<String, String> getSignMap(Object o) throws IllegalAccessException {     Map<String, String> fieldNameToValue = new HashMap<>();     for (Field f : o.getClass().getDeclaredFields()) {         System.out.println(f.getName());         for (Annotation annotation : f.getDeclaredAnnotations()) {             if (annotation.annotationType().equals(SignField.class)) {                 String value = "";                 f.setAccessible(true);                 fieldNameToValue.put(f.getName(), f.get(o).toString());             }         }     }     return fieldNameToValue; }

2.计算出签名值,这里在属性名和属性值以外加入了我的昵称以防止他人猜测,同时使用了自定义的分隔符来加强密码强度。

typescript复制代码private String getSign(Map<String, String> fieldNameToValue) {     List<String> names = new ArrayList<>(fieldNameToValue.keySet());     StringBuilder sb = new StringBuilder();     for (String name : names)         sb.append(name).append("@").append(fieldNameToValue.get(name));     System.out.println(sb.append("日暮与星辰之间").toString());     String signValue = DigestUtils.md5DigestAsHex(sb.toString().getBytes(StandardCharsets.UTF_8));     return signValue; }

  1. 找到保存签名的字段

kotlin复制代码private Field getValidateFiled(Object o) {     for (Field f : o.getClass().getDeclaredFields()) {         for (Annotation annotation : f.getDeclaredAnnotations()) {             if (annotation.annotationType().equals(ValidField.class)) {                 return f;             }         }     }     return null; }

  1. 对保存签名的字段进行赋值

ini复制代码public void sign(Object o) throws IllegalAccessException {     Map<String, String> fieldNameToValue = getSignMap(o);     if (fieldNameToValue.isEmpty()) {         return;     }     Field validateField = getValidateFiled(o);     if (validateField == null)         return;     String signValue = getSign(fieldNameToValue);     validateField.setAccessible(true);     validateField.set(o, signValue); }

  1. 对从数据库中取出的对象进行验证

scss复制代码public void valid(Object o) throws IllegalAccessException {     Map<String, String> fieldNameToValue = getSignMap(o);     if (fieldNameToValue.isEmpty()) {         return;     }     Field validateField = getValidateFiled(o);     validateField.setAccessible(true);     String signValue = getSign(fieldNameToValue);     if (!Objects.equals(signValue, validateField.get(o))) {         throw new RuntimeException("数据非法");     }  }

使用示例

对将要保存到数据库的对象进行签名

sql复制代码@Sign public Order save( Order order){     orderList.add(order);     return order; }

验证从数据库中取出的对象是否合理

scss复制代码@Validate public Order query(@ String sn){    return orderList.stream().filter(e -> e.getSn().equals(sn)).findFirst().orElse(null); }

把数据库里的未付款订单改成已付款,会发生什么的评论 (共 条)

分享到微博请遵守国家法律