成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring Boot + AOP + Jasypt,三步實現敏感數據脫敏(附完整代碼)

開發 前端
我們實現了敏感數據的透明化處理,在保證業務邏輯完整性的同時,構建了多層次的數據安全防護體系。這種方案適用于金融、醫療、電商等對數據安全要求較高的領域,能夠有效滿足??GDPR、HIPAA??等合規性要求。?

方案背景

在企業級應用開發中,用戶敏感信息(如手機號、郵箱、密碼等)的保護至關重要。Spring Boot生態提供了Jasypt加密框架與AOP編程模型的組合方案,可在不侵入業務邏輯的前提下實現數據全生命周期的脫敏管理。

實現

加密工具類

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Sm4Utils {
    private static Pattern p = Pattern.compile("\\s*|\t|\r|\n");
    private static final Logger logger = LoggerFactory.getLogger(Sm4Utils.class);

    private Sm4Utils() {
    }

    public static Sm4Utils getInstance() {
        return new Sm4Utils();
    }

    public static String encryptSub(String plaintext, String seed, String offset, String sm2Type) {
        logger.info("SM4加密開始...");
        if ("CBC".equals(sm2Type)) {
            return encryptDataCbc(plaintext, seed, offset);
        } elseif ("ECB".equals(sm2Type)) {
            return encryptDataEcb(plaintext, seed);
        } else {
            logger.error("不存在此加密方式");
            return"";
        }
    }

    public static String decryptSub(String ciphertext, String seed, String offset, String sm4Type) {
        logger.info("SM4解密開始...");
        if ("CBC".equals(sm4Type)) {
            return decryptDataCbc(ciphertext, seed, offset);
        } elseif ("ECB".equals(sm4Type)) {
            return decryptDataEcb(ciphertext, seed);
        } else {
            logger.error("不存在此解密方式");
            return"";
        }
    }

    public static String encryptDataEcb(String plainText, String key) {
        try {
            Sm4Context ctx = new Sm4Context();
            ctx.isPadding = true;
            ctx.mode = 1;
            byte[] keyBytes = key.getBytes();
            Sm4 sm4 = new Sm4();
            sm4.sm4_setkey_enc(ctx, keyBytes);
            byte[] encrypted = sm4.sm4_crypt_ecb(ctx, plainText.getBytes("UTF-8"));
            String cipherText = (new BASE64Encoder()).encode(encrypted);
            if (cipherText != null && cipherText.trim().length() > 0) {
                Matcher m = p.matcher(cipherText);
                cipherText = m.replaceAll("");
            }

            return cipherText;
        } catch (Exception var9) {
            logger.error("ECB加密失敗", var9);
            return"";
        }
    }

    public static String decryptDataEcb(String cipherText, String key) {
        try {
            Sm4Context ctx = new Sm4Context();
            ctx.isPadding = true;
            ctx.mode = 0;
            byte[] keyBytes = key.getBytes();
            Sm4 sm4 = new Sm4();
            sm4.sm4_setkey_dec(ctx, keyBytes);
            byte[] decrypted = sm4.sm4_crypt_ecb(ctx, (new BASE64Decoder()).decodeBuffer(cipherText));
            return new String(decrypted, "UTF-8");
        } catch (Exception var7) {
            logger.error("ECB解密失敗", var7);
            return"";
        }
    }

    public static String encryptDataCbc(String plainText, String key, String offset) {
        try {
            Sm4Context ctx = new Sm4Context();
            ctx.isPadding = true;
            ctx.mode = 1;
            byte[] keyBytes = key.getBytes();
            byte[] ivBytes = offset.getBytes();
            Sm4 sm4 = new Sm4();
            sm4.sm4_setkey_enc(ctx, keyBytes);
            byte[] encrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, plainText.getBytes("UTF-8"));
            String cipherText = (new BASE64Encoder()).encode(encrypted);
            if (cipherText != null && cipherText.trim().length() > 0) {
                Matcher m = p.matcher(cipherText);
                cipherText = m.replaceAll("");
            }

            return cipherText;
        } catch (Exception var11) {
            logger.error("CBC加密失敗", var11);
            return"";
        }
    }

    public static String decryptDataCbc(String cipherText, String key, String offset) {
        try {
            Sm4Context ctx = new Sm4Context();
            ctx.isPadding = true;
            ctx.mode = 0;
            byte[] keyBytes = key.getBytes();
            byte[] ivBytes = offset.getBytes();
            Sm4 sm4 = new Sm4();
            sm4.sm4_setkey_dec(ctx, keyBytes);
            byte[] decrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, (new BASE64Decoder()).decodeBuffer(cipherText));
            return new String(decrypted, "UTF-8");
        } catch (Exception var9) {
            logger.error("CBC解密失敗", var9);
            return"";
        }
    }
}

自定義算法

public class Sm4Context {
    public int mode = 1;
    public long[] sk = new long[32];
    public boolean isPadding = true;

    public Sm4Context() {
    }
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;


public class Sm4 {

    public static final byte[] SboxTable = new byte[]{-42, -112, -23, -2, -52, -31, 61, -73, 22, -74, 20, -62, 40, -5, 44, 5, 43, 103, -102, 118, 42, -66, 4, -61, -86, 68, 19, 38, 73, -122, 6, -103, -100, 66, 80, -12, -111, -17, -104, 122, 51, 84, 11, 67, -19, -49, -84, 98, -28, -77, 28, -87, -55, 8, -24, -107, -128, -33, -108, -6, 117, -113, 63, -90, 71, 7, -89, -4, -13, 115, 23, -70, -125, 89, 60, 25, -26, -123, 79, -88, 104, 107, -127, -78, 113, 100, -38, -117, -8, -21, 15, 75, 112, 86, -99, 53, 30, 36, 14, 94, 99, 88, -47, -94, 37, 34, 124, 59, 1, 33, 120, -121, -44, 0, 70, 87, -97, -45, 39, 82, 76, 54, 2, -25, -96, -60, -56, -98, -22, -65, -118, -46, 64, -57, 56, -75, -93, -9, -14, -50, -7, 97, 21, -95, -32, -82, 93, -92, -101, 52, 26, 85, -83, -109, 50, 48, -11, -116, -79, -29, 29, -10, -30, 46, -126, 102, -54, 96, -64, 41, 35, -85, 13, 83, 78, 111, -43, -37, 55, 69, -34, -3, -114, 47, 3, -1, 106, 114, 109, 108, 91, 81, -115, 27, -81, -110, -69, -35, -68, 127, 17, -39, 92, 65, 31, 16, 90, -40, 10, -63, 49, -120, -91, -51, 123, -67, 45, 116, -48, 18, -72, -27, -76, -80, -119, 105, -105, 74, 12, -106, 119, 126, 101, -71, -15, 9, -59, 110, -58, -124, 24, -16, 125, -20, 58, -36, 77, 32, 121, -18, 95, 62, -41, -53, 57, 72};
    public static final int[] FK = new int[]{-1548633402, 1453994832, 1736282519, -1301273892};
    public static final int[] CK = new int[]{462357, 472066609, 943670861, 1415275113, 1886879365, -1936483679, -1464879427, -993275175, -521670923, -66909679, 404694573, 876298825, 1347903077, 1819507329, -2003855715, -1532251463, -1060647211, -589042959, -117504499, 337322537, 808926789, 1280531041, 1752135293, -2071227751, -1599623499, -1128019247, -656414995, -184876535, 269950501, 741554753, 1213159005, 1684763257};

    public Sm4() {
    }

    private long GET_ULONG_BE(byte[] b, int i) {
        long n = (long)(b[i] & 255) << 24 | (long)((b[i + 1] & 255) << 16) | (long)((b[i + 2] & 255) << 8) | (long)(b[i + 3] & 255) & 4294967295L;
        return n;
    }

    private void PUT_ULONG_BE(long n, byte[] b, int i) {
        b[i] = (byte)((int)(255L & n >> 24));
        b[i + 1] = (byte)((int)(255L & n >> 16));
        b[i + 2] = (byte)((int)(255L & n >> 8));
        b[i + 3] = (byte)((int)(255L & n));
    }

    private long SHL(long x, int n) {
        return (x & -1L) << n;
    }

    private long ROTL(long x, int n) {
        return this.SHL(x, n) | x >> 32 - n;
    }

    private void SWAP(long[] sk, int i) {
        long t = sk[i];
        sk[i] = sk[31 - i];
        sk[31 - i] = t;
    }

    private byte sm4Sbox(byte inch) {
        int i = inch & 255;
        byte retVal = SboxTable[i];
        return retVal;
    }

    private long sm4Lt(long ka) {
        long bb = 0L;
        long c = 0L;
        byte[] a = new byte[4];
        byte[] b = new byte[4];
        this.PUT_ULONG_BE(ka, a, 0);
        b[0] = this.sm4Sbox(a[0]);
        b[1] = this.sm4Sbox(a[1]);
        b[2] = this.sm4Sbox(a[2]);
        b[3] = this.sm4Sbox(a[3]);
        bb = this.GET_ULONG_BE(b, 0);
        c = bb ^ this.ROTL(bb, 2) ^ this.ROTL(bb, 10) ^ this.ROTL(bb, 18) ^ this.ROTL(bb, 24);
        return c;
    }

    private long sm4F(long x0, long x1, long x2, long x3, long rk) {
        return x0 ^ this.sm4Lt(x1 ^ x2 ^ x3 ^ rk);
    }

    private long sm4CalciRK(long ka) {
        long bb = 0L;
        long rk = 0L;
        byte[] a = new byte[4];
        byte[] b = new byte[4];
        this.PUT_ULONG_BE(ka, a, 0);
        b[0] = this.sm4Sbox(a[0]);
        b[1] = this.sm4Sbox(a[1]);
        b[2] = this.sm4Sbox(a[2]);
        b[3] = this.sm4Sbox(a[3]);
        bb = this.GET_ULONG_BE(b, 0);
        rk = bb ^ this.ROTL(bb, 13) ^ this.ROTL(bb, 23);
        return rk;
    }

    private void sm4_setkey(long[] SK, byte[] key) {
        long[] MK = new long[4];
        long[] k = new long[36];
        int i = 0;
        MK[0] = this.GET_ULONG_BE(key, 0);
        MK[1] = this.GET_ULONG_BE(key, 4);
        MK[2] = this.GET_ULONG_BE(key, 8);
        MK[3] = this.GET_ULONG_BE(key, 12);
        k[0] = MK[0] ^ (long)FK[0];
        k[1] = MK[1] ^ (long)FK[1];
        k[2] = MK[2] ^ (long)FK[2];

        for(k[3] = MK[3] ^ (long)FK[3]; i < 32; ++i) {
            k[i + 4] = k[i] ^ this.sm4CalciRK(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ (long)CK[i]);
            SK[i] = k[i + 4];
        }

    }

    private void sm4_one_round(long[] sk, byte[] input, byte[] output) {
        int i = 0;
        long[] ulbuf = new long[36];
        ulbuf[0] = this.GET_ULONG_BE(input, 0);
        ulbuf[1] = this.GET_ULONG_BE(input, 4);
        ulbuf[2] = this.GET_ULONG_BE(input, 8);

        for(ulbuf[3] = this.GET_ULONG_BE(input, 12); i < 32; ++i) {
            ulbuf[i + 4] = this.sm4F(ulbuf[i], ulbuf[i + 1], ulbuf[i + 2], ulbuf[i + 3], sk[i]);
        }

        this.PUT_ULONG_BE(ulbuf[35], output, 0);
        this.PUT_ULONG_BE(ulbuf[34], output, 4);
        this.PUT_ULONG_BE(ulbuf[33], output, 8);
        this.PUT_ULONG_BE(ulbuf[32], output, 12);
    }

    private byte[] padding(byte[] input, int mode) {
        if (input == null) {
            return null;
        } else {
            byte[] ret = (byte[])null;
            if (mode == 1) {
                int p = 16 - input.length % 16;
                ret = new byte[input.length + p];
                System.arraycopy(input, 0, ret, 0, input.length);

                for(int i = 0; i < p; ++i) {
                    ret[input.length + i] = (byte)p;
                }
            } else {
                int p = input[input.length - 1];
                ret = new byte[input.length - p];
                System.arraycopy(input, 0, ret, 0, input.length - p);
            }

            return ret;
        }
    }

    public void sm4_setkey_enc(Sm4Context ctx, byte[] key) throws Exception {
        if (ctx == null) {
            throw new Exception("ctx is null!");
        } elseif (key != null && key.length == 16) {
            ctx.mode = 1;
            this.sm4_setkey(ctx.sk, key);
        } else {
            throw new Exception("key error!");
        }
    }

    public void sm4_setkey_dec(Sm4Context ctx, byte[] key) throws Exception {
        if (ctx == null) {
            throw new Exception("ctx is null!");
        } elseif (key != null && key.length == 16) {
            ctx.mode = 0;
            this.sm4_setkey(ctx.sk, key);

            for(int i = 0; i < 16; ++i) {
                this.SWAP(ctx.sk, i);
            }

        } else {
            throw new Exception("key error!");
        }
    }

    public byte[] sm4_crypt_ecb(Sm4Context ctx, byte[] input) throws Exception {
        if (input == null) {
            throw new Exception("input is null!");
        } else {
            if (ctx.isPadding && ctx.mode == 1) {
                input = this.padding(input, 1);
            }

            int length = input.length;
            ByteArrayInputStream bins = new ByteArrayInputStream(input);

            ByteArrayOutputStream bous;
            byte[] output;
            for(bous = new ByteArrayOutputStream(); length > 0; length -= 16) {
                output = new byte[16];
                byte[] out = new byte[16];
                bins.read(output);
                this.sm4_one_round(ctx.sk, output, out);
                bous.write(out);
            }

            output = bous.toByteArray();
            if (ctx.isPadding && ctx.mode == 0) {
                output = this.padding(output, 0);
            }

            bins.close();
            bous.close();
            return output;
        }
    }

    public byte[] sm4_crypt_cbc(Sm4Context ctx, byte[] iv, byte[] input) throws Exception {
        if (iv != null && iv.length == 16) {
            if (input == null) {
                throw new Exception("input is null!");
            } else {
                if (ctx.isPadding && ctx.mode == 1) {
                    input = this.padding(input, 1);
                }

                int length = input.length;
                ByteArrayInputStream bins = new ByteArrayInputStream(input);
                ByteArrayOutputStream bous = new ByteArrayOutputStream();
                byte[] temp;
                byte[] out;
                int i;
                if (ctx.mode != 1) {
                    for(temp = new byte[16]; length > 0; length -= 16) {
                        out = new byte[16];
                        out = new byte[16];
                        byte[] out1 = new byte[16];
                        bins.read(out);
                        System.arraycopy(out, 0, temp, 0, 16);
                        this.sm4_one_round(ctx.sk, out, out);

                        for(i = 0; i < 16; ++i) {
                            out1[i] = (byte)(out[i] ^ iv[i]);
                        }

                        System.arraycopy(temp, 0, iv, 0, 16);
                        bous.write(out1);
                    }
                } else {
                    while(length > 0) {
                        temp = new byte[16];
                        out = new byte[16];
                        out = new byte[16];
                        bins.read(temp);

                        for(i = 0; i < 16; ++i) {
                            out[i] = (byte)(temp[i] ^ iv[i]);
                        }

                        this.sm4_one_round(ctx.sk, out, out);
                        System.arraycopy(out, 0, iv, 0, 16);
                        bous.write(out);
                        length -= 16;
                    }
                }

                temp = bous.toByteArray();
                if (ctx.isPadding && ctx.mode == 0) {
                    temp = this.padding(temp, 0);
                }

                bins.close();
                bous.close();
                return temp;
            }
        } else {
            throw new Exception("iv error!");
        }
    }
}

測試

String s = Sm4Utils.encryptSub("一安未來", "@yianweilai#$%^&", "1234567890123456", "CBC");
System.out.println(s);
System.out.println(Sm4Utils.decryptSub(s, "@yianweilai#$%^&", "1234567890123456", "CBC"));

cpudYch9orpFFc0uSxhg7g==
一安未來

String s1 = Sm4Utils.encryptSub("一安未來", "@yianweilai#$%^&", "", "ECB");
System.out.println(s1);
System.out.println(Sm4Utils.decryptSub(s1, "@yianweilai#$%^&", "", "ECB"));
  
RxV6VK9bNKEBqNFq0uVuqQ==
一安未來

擴展

引入依賴

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

自定義注解

字段加密注解(用于標記字段是否加密)
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptFields {
    String[] value() default "";
}
方法加密注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedEncrypt {
}
方法解密注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedDecrypt {
}
實體類
@AllArgsConstructor
@Data
public class User {
    private Long id;
    private String username;
    private String password;
    @EncryptFields
    private String email;
}

AOP 切面實現

加密切面
@Aspect
@Component
public class EncryptAspect {
    @Autowired
    private StringEncryptor encryptor;

    @Around("@annotation(NeedEncrypt)")
    public Object encryptData(ProceedingJoinPoint joinPoint) throws Throwable {
        processParameters(joinPoint.getArgs());
        return joinPoint.proceed();
    }

    private void processParameters(Object[] args) {
        Arrays.stream(args)
             .filter(Objects::nonNull)
             .forEach(this::encryptObject);
    }

    private void encryptObject(Object object) {
        ReflectionUtils.doWithFields(object.getClass(), field -> {
            if (field.isAnnotationPresent(EncryptFields.class)) {
                field.setAccessible(true);
                ReflectionUtils.setField(field, object, encryptor.encrypt(field.get(object).toString()));
            }
        });
    }
}
解密切面
@Aspect
@Component
public class DecryptAspect {

    @Around("@annotation(NeedDecrypt)")
    public Object decryptData(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        return processResult(result);
    }

    private Object processResult(Object result) {
        if (result instanceof Collection) {
            ((Collection<?>) result).forEach(this::decryptObject);
        } elseif (result != null) {
            decryptObject(result);
        }
        return result;
    }

    private void decryptObject(Object object) {
        ReflectionUtils.doWithFields(object.getClass(), field -> {
            if (field.isAnnotationPresent(EncryptFields.class)) {
                field.setAccessible(true);
                ReflectionUtils.setField(field, object, Sm4Utils.decryptSub(field.get(object).toString(), "@yianweilai#$%^&", "", "ECB"));
            }
        });
    }
}

使用示例

@RestController
@RequestMapping("/api")
public class Sm4Controller {

    @NeedEncrypt
    @PostMapping("/user")
    public User saveUser(@RequestBody User user) {
        log.info("saveUser params:{}", user);
        // 業務處理邏輯
        return user;
    }

    @NeedDecrypt
    @GetMapping("/user")
    public User getUser() {
        // 從數據庫獲取加密數據
        User user = new User();
        user.setId(1L);
        user.setUsername("一安未來");
        user.setEmail(Sm4Utils.encryptSub("yianweilai@163.com", "@yianweilai#$%^&", "", "ECB"));
        log.info("getUser user:{}", user);
        return user;
    }
}


圖片圖片

總結

通過AOPJasypt的結合,我們實現了敏感數據的透明化處理,在保證業務邏輯完整性的同時,構建了多層次的數據安全防護體系。這種方案適用于金融、醫療、電商等對數據安全要求較高的領域,能夠有效滿足GDPR、HIPAA等合規性要求。

責任編輯:武曉燕 來源: 一安未來
相關推薦

2023-06-06 08:51:06

2024-09-27 12:27:31

2023-10-07 08:34:27

項目API接口

2020-10-25 09:04:46

數據加密數據泄露攻擊

2024-01-01 14:19:11

2021-10-28 09:42:38

代碼編碼開發

2009-11-16 13:04:04

PHP上傳文件代碼

2021-09-16 10:11:15

Dataphin 數據保護

2010-09-25 08:55:29

2023-10-23 10:39:05

2025-04-08 03:00:00

SpringDocker容器

2013-12-26 13:10:38

大數據NoSQL

2023-07-21 12:48:37

2025-04-03 07:06:35

2010-04-20 20:53:35

實現網絡負載均衡

2010-02-26 16:16:15

2010-08-12 10:10:37

FlexMapABC

2023-06-27 07:26:36

汽車之家敏感數據治理

2021-03-02 07:02:45

Linux操作系統

2009-11-10 12:55:26

VB.NET三維模型
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美日韩成人 | 国产乱一区二区三区视频 | 国产女人与拘做受免费视频 | 亚洲综合二区 | 一区二区三区在线免费观看 | 在线视频国产一区 | 日韩在线一区二区 | 黄网站涩免费蜜桃网站 | 日本午夜在线视频 | 国产日韩欧美精品一区二区三区 | 国产91在线 | 亚洲 | 精品久久久久久久人人人人传媒 | 97精品久久| 欧美日韩1区 | 成人精品鲁一区一区二区 | 狠狠躁夜夜躁人人爽天天高潮 | 国产精品日韩在线观看一区二区 | 国产不卡在线播放 | 精品国产乱码久久久久久88av | 超级乱淫av片免费播放 | 日韩一三区 | 久久中文字幕在线 | 亚洲精品一区二区三区蜜桃久 | 久久九九99 | 久久精品国产一区二区电影 | 在线视频 亚洲 | 国产精品久久久久久久久久久久冷 | 国产日产精品一区二区三区四区 | 一区二区三区在线看 | 亚洲一区二区三区在线播放 | 午夜影院在线观看 | 国产精品18hdxxxⅹ在线 | 国产一二三区精品视频 | 国产一级久久久久 | 欧美专区在线视频 | 精品久久久久久中文字幕 | 91一区二区三区 | 国产精品毛片在线 | 国产精品1区2区3区 中文字幕一区二区三区四区 | 国产精品入口麻豆www | 欧美性乱 |