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

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

时间:2022-10-23 15:12:01浏览次数:40  
标签:return String fieldNameToValue 数据库 Object private 付款 改成 public

导言

不知道大家在网上购物的时候,有没有这样的念头,如果能把未付款的订单偷偷用一条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;
}
  1. 找到保存签名的字段
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. 对保存签名的字段进行赋值
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. 对从数据库中取出的对象进行验证
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);
}

标签:return,String,fieldNameToValue,数据库,Object,private,付款,改成,public
From: https://blog.51cto.com/u_15815141/5787315

相关文章

  • MySQL数据库DDL详情介绍
    DDL-表操作showtables;–查看当前数据库所有的表createtable表名(字段字段类型,字段字段类型);–创建表(最后一个字段类型后不需要加逗号)desc表名;–查看当前表有......
  • 数据库连接池_druid工具类和数据库连接池_druid工具类测试
    数据库连接池_druid工具类:1.定义工具类定义一个类JDBCUtils提供静态代码块加载配置文件,初始化连接池对象提供方法1.获取连接方法:通过数据库连接池获取连接2.释放资源3......
  • 数据库(七)多表查询
    前言本文为在霍格沃兹测试开发学社的学习经历分享,希望有志同道合的小伙伴可以一起交流技术,一起进步~多表查询概念多表就是在数据库设计中使用多张表格来实现数据存储......
  • 数据库(四)DQL
    DQL查询语言单表查询语法:select*from表名;字段查询select列名from表名;起别名去掉重复信息selectdistinct列名from表名;注意:distinct要放......
  • 数据库(五)聚合函数
    聚合函数概念用来做纵向运算的,可以针对某列的值进行运算,返回一个单一的值,可以计算平均工资等常见聚合函数:COUNT():统计指定列不为NULL的记录行数MAX():计算指定列......
  • 数据库(六)约束
    sql约束概念对表中的数据进行进一步的限制保证数据的正确性、有效性、完整性违反约束的不正确数据无法插入到表中常见约束主键:PRIMARYKEY非空:NOTNULL......
  • 数据库(三)DML
    DML操作语言插入语法格式:示例:--选择数据库为当前数据库USEhogwarts_stu;--创建表CREATETABLEuser(idint,namevarchar(20),ageint,sexchar(1......
  • 数据库(一)
    数据库基本概念是一个以某种有组织的方式存储的数据集合是存储和管理数据的仓库本质是一个文件系统DBMS(数据库管理系统)数据库分类关系型数据库(RDB)有关系......
  • Django-静态文件配置,链接数据库,request对象方法
    目录-静态文件配置--含义--配置方法--动态解析---含义---实现---示例-pycharm链接MySQL-Django链接MySQL--代码配置--代码声明-request对象常用方法--示例......
  • SpringBoot 创建项目连接mysql数据库
    Spring 创建项目1.创建一个springboot项目2.点击File---- New---- project项目名称可以随便填写...3. Springboot版本尽量不要最新版,怕你们驾驭不了......