Skip to content

Commit 4ebeb2d

Browse files
committed
refactor: ID confusion with CRC8 check sum
1 parent 449387d commit 4ebeb2d

4 files changed

Lines changed: 123 additions & 7 deletions

File tree

blog-admin/src/main/java/com/hackyle/blog/admin/module/article/service/impl/IdConfusionServiceImpl.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package com.hackyle.blog.admin.module.article.service.impl;
22

33
import com.hackyle.blog.admin.module.article.service.IdConfusionService;
4+
import com.hackyle.blog.common.exception.BizException;
5+
import com.hackyle.blog.common.util.CrcUtils;
46
import org.springframework.beans.factory.annotation.Value;
57
import org.springframework.stereotype.Service;
68

9+
import java.util.Objects;
10+
711
/**
812
* ID混淆
913
*/
@@ -19,6 +23,7 @@ public class IdConfusionServiceImpl implements IdConfusionService {
1923
* 先模62,从Base62字符数组中取得一个字符
2024
* 再除62,直到num小于0时停止
2125
* 得到一个唯一串
26+
* 计算CRC8校验码
2227
*/
2328
public String encode(long id) {
2429
if(id < 0) {
@@ -35,20 +40,35 @@ public String encode(long id) {
3540
//while (sb.length() < CODE_LENGTH) {
3641
// sb.append('0'); // 也可填充随机字符
3742
//}
38-
return sb.reverse().toString(); // 高位在前
43+
44+
//计算CRC8校验码
45+
String code = sb.reverse().toString();
46+
char crc8CheckSum = CrcUtils.crc8CheckSum(code);
47+
if(Character.isWhitespace(crc8CheckSum)) {
48+
throw new BizException("CRC8 Check Sum 计算失败");
49+
}
50+
51+
return code + crc8CheckSum; // 高位在前
3952
}
4053

4154
/**
55+
* 检查校验码
4256
* 依次遍历唯一串的每个字符c
4357
* 从字符数组中找到该个字符c所在的下标i
4458
* id = id*62+i
4559
* 最终解析出的值即为原始id
4660
*/
4761
public long decode(String code) {
62+
String body = code.substring(0, code.length() - 1);
63+
char crc8CheckSum = code.charAt(code.length() - 1);
64+
if(!Objects.equals(crc8CheckSum, CrcUtils.crc8CheckSum(body))) {
65+
throw new BizException("CRC8 Check Sum 校验失败");
66+
}
67+
4868
long id = 0;
4969
int baseSize = idConfusionBaseChar.length();
5070

51-
for (char cc : code.toCharArray()) {
71+
for (char cc : body.toCharArray()) {
5272
Integer mod = null;
5373
for (int j = 0; j < baseSize; j++) {
5474
if(cc == idConfusionBaseChar.charAt(j)) {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.hackyle.blog.common.util;
2+
3+
import java.nio.charset.StandardCharsets;
4+
import java.util.Random;
5+
6+
/**
7+
* CRC(Cyclic Redundancy Check,循环冗余校验)算法工具类
8+
*/
9+
public class CrcUtils {
10+
11+
private static final int POLY = 0x07; // CRC-8 标准多项式
12+
13+
//用于生成校验位的字符集
14+
//private static final char[] BASE32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".toCharArray();
15+
private static final char[] BASE62 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
16+
17+
/**
18+
* 标准CRC8
19+
* 多项式 0x07
20+
* 初始值 0x00
21+
*/
22+
private static byte crc8(byte[] data) {
23+
int crc = 0x00;
24+
25+
for (byte b : data) {
26+
crc ^= (b & 0xFF);
27+
for (int i = 0; i < 8; i++) {
28+
if ((crc & 0x80) != 0) {
29+
crc = ((crc << 1) ^ POLY) & 0xFF;
30+
} else {
31+
crc = (crc << 1) & 0xFF;
32+
}
33+
}
34+
}
35+
return (byte) crc;
36+
}
37+
38+
/**
39+
* CRC8校验码编码
40+
* @param data 原始数据
41+
* @return 校验
42+
*/
43+
public static char crc8CheckSum(String data) {
44+
//**isEmpty()**:判断是不是“空字符串”
45+
//**isBlank()**:判断是不是“空白字符串”(包含空格、制表符、换行符这些)
46+
if(data == null || data.trim().isBlank()) {
47+
throw new IllegalArgumentException("CRC8 Encoded String is null or empty");
48+
}
49+
byte crc8 = crc8(data.getBytes(StandardCharsets.UTF_8));
50+
return BASE62[(crc8 & 0xFF) % BASE62.length];
51+
}
52+
53+
public static void main(String[] args) {
54+
Random random = new Random();
55+
for (int i = 0; i < 10; i++) {
56+
String str = "Asdfaddu1234" + random.nextLong();
57+
char crc8CheckSum = CrcUtils.crc8CheckSum(str);
58+
System.out.println(crc8CheckSum);
59+
60+
String code = str + crc8CheckSum;
61+
62+
int randIndex = random.nextInt(3);
63+
String body = code.substring(0, code.length() - randIndex);
64+
char check = code.charAt(code.length() - 1);
65+
boolean result = CrcUtils.crc8CheckSum(body) == check;
66+
//如果randIndex=1,则校验结果应该为true
67+
System.out.println("====== randIndex=" + randIndex + " result=" +result);
68+
69+
}
70+
}
71+
72+
}

blog-customer/src/main/java/com/hackyle/blog/customer/handler/GlobalExceptionHandler.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,16 @@
4141
public class GlobalExceptionHandler {
4242

4343
//@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
44-
@ResponseBody
4544
@ExceptionHandler(BizException.class)
46-
public ApiResponse<Exception> bizExceptionHandler(HttpServletRequest request, BizException exception) {
45+
public ModelAndView bizExceptionHandler(HttpServletRequest request, BizException exception) {
4746
log.error("出现BizException异常:", exception);
48-
return ApiResponse.fail(HttpStatus.UNPROCESSABLE_ENTITY.value(), exception.getMessage());
47+
48+
ModelAndView modelAndView = new ModelAndView();
49+
modelAndView.addObject("code", HttpStatus.NOT_FOUND);
50+
modelAndView.addObject("message", exception.getMessage());
51+
modelAndView.addObject("url", request.getRequestURL());
52+
modelAndView.setViewName("common/404");
53+
return modelAndView;
4954
}
5055

5156
//@ResponseStatus(HttpStatus.NOT_FOUND)

blog-customer/src/main/java/com/hackyle/blog/customer/module/article/service/impl/IdConfusionServiceImpl.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.hackyle.blog.customer.module.article.service.impl;
22

33
import cn.hutool.core.collection.CollectionUtil;
4+
import com.hackyle.blog.common.exception.BizException;
5+
import com.hackyle.blog.common.util.CrcUtils;
46
import com.hackyle.blog.customer.module.article.service.IdConfusionService;
57
import org.springframework.beans.factory.annotation.Value;
68
import org.springframework.stereotype.Service;
79

810
import java.util.ArrayList;
911
import java.util.List;
12+
import java.util.Objects;
1013

1114
/**
1215
* ID混淆
@@ -27,6 +30,7 @@ public class IdConfusionServiceImpl implements IdConfusionService {
2730
* 先模62,从Base62字符数组中取得一个字符
2831
* 再除62,直到num小于0时停止
2932
* 得到一个唯一串
33+
* 计算CRC8校验码
3034
*/
3135
public String encode(long id) {
3236
if(id < 0) {
@@ -43,20 +47,35 @@ public String encode(long id) {
4347
//while (sb.length() < CODE_LENGTH) {
4448
// sb.append('0'); // 也可填充随机字符
4549
//}
46-
return sb.reverse().toString(); // 高位在前
50+
51+
//计算CRC8校验码
52+
String code = sb.reverse().toString();
53+
char crc8CheckSum = CrcUtils.crc8CheckSum(code);
54+
if(Character.isWhitespace(crc8CheckSum)) {
55+
throw new BizException("CRC8 Check Sum 计算失败");
56+
}
57+
58+
return code + crc8CheckSum; // 高位在前
4759
}
4860

4961
/**
62+
* 检查校验码
5063
* 依次遍历唯一串的每个字符c
5164
* 从字符数组中找到该个字符c所在的下标i
5265
* id = id*62+i
5366
* 最终解析出的值即为原始id
5467
*/
5568
public long decode(String code) {
69+
String body = code.substring(0, code.length() - 1);
70+
char crc8CheckSum = code.charAt(code.length() - 1);
71+
if(!Objects.equals(crc8CheckSum, CrcUtils.crc8CheckSum(body))) {
72+
throw new BizException("CRC8 Check Sum 校验失败");
73+
}
74+
5675
long id = 0;
5776
int baseSize = baseChar.length();
5877

59-
for (char cc : code.toCharArray()) {
78+
for (char cc : body.toCharArray()) {
6079
Integer mod = null;
6180
for (int j = 0; j < baseSize; j++) {
6281
if(cc == baseChar.charAt(j)) {

0 commit comments

Comments
 (0)