Java開發(fā)如何把數(shù)據(jù)庫里的未付款訂單改成已付款
導言
不知道大家在網(wǎng)上購物的時候,有沒有這樣的念頭,如果能把未付款的訂單偷偷用一條SQL改成已付款,該多么美好啊。那么在實際開發(fā)過程中,我們應當如何保證數(shù)據(jù)庫里的數(shù)據(jù)在保存后不會被偷偷更改?
理論
在介紹具體的內(nèi)容之間,先介紹MD5算法,簡單的來說,MD5能把任意大小、長度的數(shù)據(jù)轉(zhuǎn)換成固定長度的一串字符,經(jīng)常玩大型游戲的朋友應該都注意到過,各種補丁包、端游客戶端之類的大型文件一般都附有一個MD5值,用于確保你下載文件的完整性。那么在這里,我們可以借鑒其思想,對訂單的某些屬性進行加密計算,得出來一個 MD5值一并保存在數(shù)據(jù)庫當中。從數(shù)據(jù)庫取出數(shù)據(jù)后第一時間進行校驗,如果有異常更改,那么及時拋出異常進行人工處理。
實現(xiàn)
道理我都懂,但是我要如何做呢,別急,且聽我一一道來。
這種需求聽起來并不強綁定于某個具體的業(yè)務(wù)需求,這就要用到了我們熟悉的鼎鼎有名的AOP(面向切面編程)來實現(xiàn)。
首先定義四個類型的注解作為AOP的切入點。@Sign
和@Validate
都是作用在方法層面的,分別用于對方法的入?yún)⑦M行加簽和驗證方法的返回值的簽名。@SignField
用于注解關(guān)鍵的不容篡改的字段。@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就是關(guān)鍵字段,絕不能允許有人在落單到數(shù)據(jù)庫后對這些字段偷偷篡改。
public class Order { @SignField private String sn; @SignField private String amt; @SignField private int status; @SignField private int userId; @ValidField private String sign; }
下面就到了重頭戲的部分,如何通過AOP來進行實現(xiàn)。
1. 定義切入點
@Pointcut("execution(@com.example.demo.annotations.Sign * *(..))") public void signPointCut() { } @Pointcut("execution(@com.example.demo.annotations.Validate * *(..))") public void validatePointCut() { }
2.環(huá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. 簽名的實現(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.計算出簽名值,這里在屬性名和屬性值以外加入了我的昵稱以防止他人猜測,同時使用了自定義的分隔符來加強密碼強度。
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); }
對從數(shù)據(jù)庫中取出的對象進行驗證
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ù)非法"); } }
使用示例
對將要保存到數(shù)據(jù)庫的對象進行簽名
@Sign public Order save( Order order){ orderList.add(order); return order; }
驗證從數(shù)據(jù)庫中取出的對象是否合理
@Validate public Order query(@ String sn){ return orderList.stream().filter(e -> e.getSn().equals(sn)).findFirst().orElse(null); }
到此這篇關(guān)于把數(shù)據(jù)庫里的未付款訂單改成已付款,會發(fā)生什么的文章就介紹到這了,更多相關(guān)數(shù)據(jù)庫未付款訂單改成已付款內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot打包實現(xiàn)項目JAR包和依賴JAR包分離
這篇文章主要介紹了springboot打包實現(xiàn)項目JAR包和依賴JAR包分離,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02Java?IO及BufferedReader.readline()出現(xiàn)的Bug
這篇文章主要介紹了Java?IO及BufferedReader.readline()出現(xiàn)的Bug,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12