Java开发如何把数据库里的未付款订单改成已付款

 更新时间:2022年11月14日 14:50:35   作者:肥肥技术宅  
这篇文章主要介绍了Java开发如何把数据库里的未付款订单改成已付款,先介绍MD5算法,简单的来说,MD5能把任意大小、长度的数据转换成固定长度的一串字符,实现思路非常简单需要的朋友可以参考下

导言

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

理论

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

实现

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

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

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sign {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Validate {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SignField {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidField {
}

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

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. 定义切入点

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

2.环绕切入点

@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.获取需要签名字段

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

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;
}

找到保存签名的字段

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;
}

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

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);
}

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

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("数据非法");
    }
 
}

使用示例

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

@Sign
public Order save( Order order){
    orderList.add(order);
    return order;
}

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

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

到此这篇关于把数据库里的未付款订单改成已付款,会发生什么的文章就介绍到这了,更多相关数据库未付款订单改成已付款内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Maven统一版本管理的实现

    Maven统一版本管理的实现

    在使用Maven多模块结构工程时,配置版本是一个比较头疼的事,本文主要介绍了Maven统一版本管理的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Spring cloud Eureka注册中心搭建的方法

    Spring cloud Eureka注册中心搭建的方法

    这篇文章主要介绍了Spring cloud Eureka注册中心搭建的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • 详解java_ 集合综合案例:斗地主

    详解java_ 集合综合案例:斗地主

    这篇文章主要介绍了java_ 集合综合案例:斗地主,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Gradle的基本使用

    Gradle的基本使用

    这篇文章主要介绍了Gradle的基本使用方法,帮助大家更好的理解和学习Gradle的相关知识,感兴趣的朋友可以了解下
    2021-03-03
  • 关于log4j漏洞修复解决方案及源码编译

    关于log4j漏洞修复解决方案及源码编译

    Log4j 是Apache为Java提供的日志管理工具。他与System.out.println()的作用相似,用来跟踪、调试、维护程序。这篇文章主要介绍了关于log4j漏洞修复解决方案及源码编译,需要的朋友可以参考下
    2021-12-12
  • 解决IDEA安装插件搜索不到插件的问题

    解决IDEA安装插件搜索不到插件的问题

    这篇文章主要介绍了解决IDEA安装插件搜索不到插件的问题,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • Mybatis查询返回两个或多个参数问题

    Mybatis查询返回两个或多个参数问题

    这篇文章主要介绍了Mybatis查询返回两个或多个参数问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • Java数组操作经典例题大总结

    Java数组操作经典例题大总结

    数组是在内存中存储相同数据类型的连续的空间,声明一个数组就是在内存空间中划出一串连续的空间,下面这篇文章主要给大家介绍了关于Java数组操作经典例题的相关资料,需要的朋友可以参考下
    2022-03-03
  • 从最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate

    从最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate

    本文会介绍从一个最基本的java工程,到Web工程,到集成Spring、SpringMVC、SpringDataJPA+Hibernate,本文介绍的非常详细,具有参考借鉴价值,感兴趣的朋友一起学习吧
    2016-05-05
  • 详解java 中Spring jsonp 跨域请求的实例

    详解java 中Spring jsonp 跨域请求的实例

    这篇文章主要介绍了详解java 中Spring jsonp 跨域请求的实例的相关资料,jsonp 可用于解决主流浏览器的跨域数据访问的问题,需要的朋友可以参考下
    2017-08-08

最新评论