Java開(kāi)發(fā)如何把數(shù)據(jù)庫(kù)里的未付款訂單改成已付款
導(dǎo)言
不知道大家在網(wǎng)上購(gòu)物的時(shí)候,有沒(méi)有這樣的念頭,如果能把未付款的訂單偷偷用一條SQL改成已付款,該多么美好啊。那么在實(shí)際開(kāi)發(fā)過(guò)程中,我們應(yīng)當(dāng)如何保證數(shù)據(jù)庫(kù)里的數(shù)據(jù)在保存后不會(huì)被偷偷更改?
理論
在介紹具體的內(nèi)容之間,先介紹MD5算法,簡(jiǎn)單的來(lái)說(shuō),MD5能把任意大小、長(zhǎng)度的數(shù)據(jù)轉(zhuǎn)換成固定長(zhǎng)度的一串字符,經(jīng)常玩大型游戲的朋友應(yīng)該都注意到過(guò),各種補(bǔ)丁包、端游客戶(hù)端之類(lèi)的大型文件一般都附有一個(gè)MD5值,用于確保你下載文件的完整性。那么在這里,我們可以借鑒其思想,對(duì)訂單的某些屬性進(jìn)行加密計(jì)算,得出來(lái)一個(gè) MD5值一并保存在數(shù)據(jù)庫(kù)當(dāng)中。從數(shù)據(jù)庫(kù)取出數(shù)據(jù)后第一時(shí)間進(jìn)行校驗(yàn),如果有異常更改,那么及時(shí)拋出異常進(jìn)行人工處理。
實(shí)現(xiàn)
道理我都懂,但是我要如何做呢,別急,且聽(tīng)我一一道來(lái)。
這種需求聽(tīng)起來(lái)并不強(qiáng)綁定于某個(gè)具體的業(yè)務(wù)需求,這就要用到了我們熟悉的鼎鼎有名的AOP(面向切面編程)來(lái)實(shí)現(xiàn)。
首先定義四個(gè)類(lèi)型的注解作為AOP的切入點(diǎn)。@Sign和@Validate都是作用在方法層面的,分別用于對(duì)方法的入?yún)⑦M(jìn)行加簽和驗(yàn)證方法的返回值的簽名。@SignField用于注解關(guān)鍵的不容篡改的字段。@ValidateField用于注解保存計(jì)算后得出的簽名值。
@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 {
}以訂單的實(shí)體為例 sn,amt,status,userId就是關(guān)鍵字段,絕不能允許有人在落單到數(shù)據(jù)庫(kù)后對(duì)這些字段偷偷篡改。
public class Order {
@SignField
private String sn;
@SignField
private String amt;
@SignField
private int status;
@SignField
private int userId;
@ValidField
private String sign;
}下面就到了重頭戲的部分,如何通過(guò)AOP來(lái)進(jìn)行實(shí)現(xiàn)。
1. 定義切入點(diǎn)
@Pointcut("execution(@com.example.demo.annotations.Sign * *(..))")
public void signPointCut() {
}
@Pointcut("execution(@com.example.demo.annotations.Validate * *(..))")
public void validatePointCut() {
}2.環(huán)繞切入點(diǎn)
@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. 簽名的實(shí)現(xiàn)
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.計(jì)算出簽名值,這里在屬性名和屬性值以外加入了我的昵稱(chēng)以防止他人猜測(cè),同時(shí)使用了自定義的分隔符來(lái)加強(qiáng)密碼強(qiáng)度。
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;
}對(duì)保存簽名的字段進(jìn)行賦值
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);
}對(duì)從數(shù)據(jù)庫(kù)中取出的對(duì)象進(jìn)行驗(yàn)證
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("數(shù)據(jù)非法");
}
}使用示例
對(duì)將要保存到數(shù)據(jù)庫(kù)的對(duì)象進(jìn)行簽名
@Sign
public Order save( Order order){
orderList.add(order);
return order;
}驗(yàn)證從數(shù)據(jù)庫(kù)中取出的對(duì)象是否合理
@Validate
public Order query(@ String sn){
return orderList.stream().filter(e -> e.getSn().equals(sn)).findFirst().orElse(null);
}到此這篇關(guān)于把數(shù)據(jù)庫(kù)里的未付款訂單改成已付款,會(huì)發(fā)生什么的文章就介紹到這了,更多相關(guān)數(shù)據(jù)庫(kù)未付款訂單改成已付款內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java實(shí)現(xiàn)訂單超時(shí)未支付自動(dòng)取消的8種方法總結(jié)
- Java使用延時(shí)隊(duì)列搞定超時(shí)訂單處理的場(chǎng)景
- Java?實(shí)現(xiàn)訂單未支付超時(shí)自動(dòng)取消功能(京東商城為例)
- 基于Java創(chuàng)建一個(gè)訂單類(lèi)代碼實(shí)例
- Java實(shí)現(xiàn)商城訂單超時(shí)取消功能
- java后臺(tái)實(shí)現(xiàn)支付寶支付接口和支付寶訂單查詢(xún)接口(前端為APP)
- Java微信支付之關(guān)閉訂單
- java web在高并發(fā)和分布式下實(shí)現(xiàn)訂單號(hào)生成唯一的解決方案
相關(guān)文章
java實(shí)現(xiàn)飯店點(diǎn)菜系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)飯店點(diǎn)菜系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
springboot打包實(shí)現(xiàn)項(xiàng)目JAR包和依賴(lài)JAR包分離
這篇文章主要介紹了springboot打包實(shí)現(xiàn)項(xiàng)目JAR包和依賴(lài)JAR包分離,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
詳解在Java程序中運(yùn)用Redis緩存對(duì)象的方法
這篇文章主要介紹了在Java程序中運(yùn)用Redis緩存對(duì)象的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
Java?IO及BufferedReader.readline()出現(xiàn)的Bug
這篇文章主要介紹了Java?IO及BufferedReader.readline()出現(xiàn)的Bug,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
Java中Optional類(lèi)及orElse方法詳解
這篇文章主要為大家介紹了Java中Optional類(lèi)及orElse()方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Java簡(jiǎn)單模擬實(shí)現(xiàn)一個(gè)線(xiàn)程池
本文主要介紹了Java簡(jiǎn)單模擬實(shí)現(xiàn)一個(gè)線(xiàn)程池,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-01-01
Spring Cloud 系列之注冊(cè)中心 Eureka詳解
Netflix Eureka 是由 Netflix 開(kāi)源的一款基于 REST 的服務(wù)發(fā)現(xiàn)組件,包括 Eureka Server 及 Eureka Client。這篇文章主要介紹了Spring Cloud 系列之注冊(cè)中心 Eureka,需要的朋友可以參考下2020-11-11
在Java中如何對(duì)類(lèi)進(jìn)行排序詳解
這篇文章主要給大家介紹了關(guān)于如何在Java中使用Arrays.toString()對(duì)類(lèi)進(jìn)行排序的相關(guān)資料,文中通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08

