偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

扔掉工具類(lèi)!MyBatis 一個(gè)簡(jiǎn)單配置搞定加密、解密,好用!

開(kāi)發(fā) 開(kāi)發(fā)工具
通過(guò) MyBatis 的自定義類(lèi)型處理器、攔截器和 MyBatis-Plus 的內(nèi)置功能,我們可以輕松實(shí)現(xiàn)數(shù)據(jù)的加密解密,徹底扔掉那個(gè)笨重的工具類(lèi)。

兄弟們,在 Java 開(kāi)發(fā)的江湖里,有一個(gè)流傳已久的傳說(shuō):每個(gè)程序員的電腦里都藏著一個(gè)神秘的 EncryptUtils 工具類(lèi)。這個(gè)類(lèi)里往往塞滿(mǎn)了各種加密解密的代碼,比如 AES、DES、MD5 等算法的實(shí)現(xiàn)。每次開(kāi)發(fā)新功能時(shí),我們都要像供祖宗一樣把它請(qǐng)出來(lái),在代碼里到處調(diào)用它的 encrypt() 和 decrypt() 方法。

但是,這樣的日子真的要持續(xù)下去嗎?想象一下,你的代碼里到處都是 EncryptUtils.encrypt(user.getPhone()) 這樣的調(diào)用,就像在整潔的白襯衫上灑了一堆咖啡漬。更要命的是,當(dāng)需求變更時(shí),比如要把 AES 換成 SM4 算法,你得像掃雷一樣在代碼里一個(gè)一個(gè)地改這些調(diào)用。這不是編程,這是在給代碼 “上刑” 啊!

今天,我就要給大家介紹一個(gè) “偷懶神器”——MyBatis 的加密解密配置。有了它,你可以徹底扔掉那個(gè)笨重的工具類(lèi),讓 MyBatis 自動(dòng)幫你搞定加密解密,從此告別手動(dòng)搬磚的日子!

一、傳統(tǒng)加密方式的 “七宗罪”

在正式介紹 MyBatis 的解決方案之前,我們先來(lái)吐槽一下傳統(tǒng)加密方式的種種 “罪行”。

1. 代碼冗余到讓人頭禿

假設(shè)你有一個(gè)用戶(hù)實(shí)體類(lèi) User,里面有 phone、idCard、email 三個(gè)敏感字段需要加密。按照傳統(tǒng)方式,你得在每個(gè) set 方法里調(diào)用加密工具類(lèi):

public void setPhone(String phone) {
    this.phone = EncryptUtils.encrypt(phone);
}
public void setIdCard(String idCard) {
    this.idCard = EncryptUtils.encrypt(idCard);
}
public void setEmail(String email) {
    this.email = EncryptUtils.encrypt(email);
}

這還不算完,在查詢(xún)的時(shí)候,你還得在每個(gè) get 方法里調(diào)用解密方法。要是有 10 個(gè)這樣的實(shí)體類(lèi),每個(gè)類(lèi)有 5 個(gè)敏感字段,你就得寫(xiě) 100 次加密解密的代碼。這不是編程,這是在寫(xiě) “代碼八股文” 啊!

2. 維護(hù)成本高得離譜

有一天,產(chǎn)品經(jīng)理突然跑過(guò)來(lái)跟你說(shuō):“我們要把加密算法從 AES 換成 SM4?!?你心里咯噔一下,知道噩夢(mèng)來(lái)了。你得打開(kāi)每個(gè)實(shí)體類(lèi),把所有的 EncryptUtils.encrypt 換成 SM4EncryptUtils.encrypt。要是漏掉一個(gè)地方,后果不堪設(shè)想。更可怕的是,你還得檢查所有的 get 方法,確保解密方法也換成了對(duì)應(yīng)的 SM4 實(shí)現(xiàn)。這簡(jiǎn)直就是一場(chǎng) “代碼馬拉松”!

3. 安全隱患防不勝防

假設(shè)你的工具類(lèi)里有一個(gè) bug,比如加密后的字符串沒(méi)有進(jìn)行 Base64 編碼,直接存到數(shù)據(jù)庫(kù)里了。這時(shí)候,數(shù)據(jù)庫(kù)里就會(huì)出現(xiàn)一堆亂碼。更要命的是,這個(gè) bug 可能在測(cè)試環(huán)境里沒(méi)有被發(fā)現(xiàn),直到上線(xiàn)后才暴露出來(lái)。這時(shí)候,你就得像救火隊(duì)員一樣緊急修復(fù),還要處理已經(jīng)存到數(shù)據(jù)庫(kù)里的亂碼數(shù)據(jù)。這不是在編程,這是在玩火啊!

二、MyBatis 的 “黑魔法”:自定義類(lèi)型處理器

既然傳統(tǒng)方式這么坑爹,那 MyBatis 有什么妙招呢?答案就是 —— 自定義類(lèi)型處理器(TypeHandler)。這個(gè)家伙就像一個(gè)隱形的管家,會(huì)在數(shù)據(jù)存入數(shù)據(jù)庫(kù)之前自動(dòng)加密,在取出數(shù)據(jù)的時(shí)候自動(dòng)解密。

1. 什么是類(lèi)型處理器?

簡(jiǎn)單來(lái)說(shuō),類(lèi)型處理器就是 MyBatis 用來(lái)處理 Java 類(lèi)型和數(shù)據(jù)庫(kù)類(lèi)型之間轉(zhuǎn)換的工具。比如,當(dāng)你把一個(gè) Java 的 String 類(lèi)型存入數(shù)據(jù)庫(kù)的 VARCHAR 字段時(shí),MyBatis 會(huì)通過(guò)類(lèi)型處理器進(jìn)行轉(zhuǎn)換。默認(rèn)情況下,MyBatis 已經(jīng)內(nèi)置了很多類(lèi)型處理器,比如處理 Integer、Date 等基本類(lèi)型的。

但是,對(duì)于敏感數(shù)據(jù)的加密解密,我們需要自定義類(lèi)型處理器。這樣,MyBatis 就會(huì)在處理這些敏感字段的時(shí)候,自動(dòng)調(diào)用我們自定義的加密解密邏輯。

2. 動(dòng)手寫(xiě)一個(gè)加密解密的類(lèi)型處理器

第一步:定義加密解密工具類(lèi)

雖然我們要扔掉工具類(lèi),但這里還是需要一個(gè)簡(jiǎn)單的工具類(lèi)來(lái)實(shí)現(xiàn)加密解密邏輯。不過(guò)別擔(dān)心,這個(gè)工具類(lèi)只會(huì)在類(lèi)型處理器里使用,不會(huì)污染業(yè)務(wù)代碼。

public class EncryptUtil {
    private static final String KEY = "mySecretKey123456"; // 這里要換成你的實(shí)際密鑰
    public static String encrypt(String data) {
        // 實(shí)現(xiàn)加密邏輯,比如 AES 加密
        return AES.encrypt(data, KEY);
    }
    public static String decrypt(String encryptedData) {
        // 實(shí)現(xiàn)解密邏輯,比如 AES 解密
        return AES.decrypt(encryptedData, KEY);
    }
}

這里需要注意,密鑰 KEY 千萬(wàn)不要硬編碼在代碼里,應(yīng)該通過(guò)配置文件或者環(huán)境變量來(lái)獲取。否則,你的代碼就像光著身子在大街上裸奔,毫無(wú)安全性可言!

第二步:創(chuàng)建自定義類(lèi)型處理器

@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(String.class)
public class EncryptTypeHandler extends BaseTypeHandler<String> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        // 在設(shè)置參數(shù)時(shí)進(jìn)行加密
        String encryptedValue = EncryptUtil.encrypt(parameter);
        ps.setString(i, encryptedValue);
    }
    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 從結(jié)果集中獲取數(shù)據(jù)時(shí)進(jìn)行解密
        String encryptedValue = rs.getString(columnName);
        return EncryptUtil.decrypt(encryptedValue);
    }
    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String encryptedValue = rs.getString(columnIndex);
        return EncryptUtil.decrypt(encryptedValue);
    }
    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String encryptedValue = cs.getString(columnIndex);
        return EncryptUtil.decrypt(encryptedValue);
    }
}

這個(gè)類(lèi)型處理器繼承自 BaseTypeHandler,并重寫(xiě)了四個(gè)方法:

  • setNonNullParameter:在設(shè)置 SQL 參數(shù)時(shí)調(diào)用,用于加密數(shù)據(jù)。
  • getNullableResult:在從結(jié)果集獲取數(shù)據(jù)時(shí)調(diào)用,用于解密數(shù)據(jù)。

這里的 @MappedJdbcTypes 和 @MappedTypes 注解指定了這個(gè)類(lèi)型處理器處理的數(shù)據(jù)庫(kù)類(lèi)型和 Java 類(lèi)型。比如,這里處理的是 VARCHAR 類(lèi)型的數(shù)據(jù)庫(kù)字段和 String 類(lèi)型的 Java 對(duì)象。

第三步:在 MyBatis 中注冊(cè)類(lèi)型處理器

在 MyBatis 的配置文件 mybatis-config.xml 中添加以下配置:

<typeHandlers>
    <typeHandler handler="com.example.handler.EncryptTypeHandler"/>
</typeHandlers>

或者,如果你使用的是 Spring Boot,可以在 application.properties 中配置:

mybatis.type-handlers-package=com.example.handler

這樣,MyBatis 就知道在處理 VARCHAR 類(lèi)型的字段時(shí),要使用我們自定義的 EncryptTypeHandler 了。

3. 在實(shí)體類(lèi)中使用自定義類(lèi)型處理器

現(xiàn)在,我們可以在實(shí)體類(lèi)中使用這個(gè)類(lèi)型處理器了。比如,我們的 User 實(shí)體類(lèi)可以這樣寫(xiě):

public class User {
    private Long id;
    private String username;
    @TypeHandler(EncryptTypeHandler.class)
    private String phone;
    @TypeHandler(EncryptTypeHandler.class)
    private String idCard;
    @TypeHandler(EncryptTypeHandler.class)
    private String email;
    // 省略 getter 和 setter 方法
}

通過(guò) @TypeHandler 注解,我們告訴 MyBatis,phone、idCard、email 這三個(gè)字段需要使用 EncryptTypeHandler 進(jìn)行處理。這樣,當(dāng)我們插入或查詢(xún)用戶(hù)數(shù)據(jù)時(shí),MyBatis 會(huì)自動(dòng)幫我們加密和解密這些字段。

4. 測(cè)試一下效果

@Test
public void testEncryptAndDecrypt() {
    User user = new User();
    user.setPhone("13812345678");
    user.setIdCard("123456789012345678");
    user.setEmail("test@example.com");
    // 插入數(shù)據(jù)
    userMapper.insert(user);
    // 查詢(xún)數(shù)據(jù)
    User queriedUser = userMapper.selectById(user.getId());
    // 斷言解密后的數(shù)據(jù)是否正確
    assertEquals("13812345678", queriedUser.getPhone());
    assertEquals("123456789012345678", queriedUser.getIdCard());
    assertEquals("test@example.com", queriedUser.getEmail());
}

運(yùn)行這個(gè)測(cè)試用例,如果所有斷言都通過(guò),那就說(shuō)明我們的自定義類(lèi)型處理器生效了!

三、攔截器:讓加密解密更 “絲滑”

雖然自定義類(lèi)型處理器已經(jīng)能解決大部分問(wèn)題,但它有一個(gè)小缺點(diǎn):每個(gè)需要加密的字段都得手動(dòng)添加 @TypeHandler 注解。如果你的項(xiàng)目中有成百上千個(gè)這樣的字段,這簡(jiǎn)直就是一場(chǎng)災(zāi)難!

這時(shí)候,MyBatis 的攔截器(Interceptor)就派上用場(chǎng)了。攔截器可以在 SQL 執(zhí)行的前后進(jìn)行攔截,對(duì)參數(shù)和結(jié)果進(jìn)行統(tǒng)一處理。這樣,我們就可以通過(guò)注解來(lái)標(biāo)記需要加密的字段,而不需要在每個(gè)字段上都添加 @TypeHandler 注解了。

1. 自定義加密注解

首先,我們需要定義一個(gè)注解,用來(lái)標(biāo)記需要加密的字段:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypt {
}

這個(gè)注解可以標(biāo)記在實(shí)體類(lèi)的字段上,表示該字段需要加密。

2. 創(chuàng)建攔截器

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
publicclass EncryptInterceptor implements Interceptor {

    @Override
    publicObject intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];

        // 處理更新操作時(shí)的參數(shù)加密
        if (invocation.getMethod().getName().equals("update")) {
            encryptParameter(parameter);
        }

        // 執(zhí)行原始操作
        Object result = invocation.proceed();

        // 處理查詢(xún)操作時(shí)的結(jié)果解密
        if (invocation.getMethod().getName().equals("query")) {
            decryptResult(result);
        }

        return result;
    }

    privatevoid encryptParameter(Object parameter) {
        if (parameter == null) {
            return;
        }

        if (parameter instanceof Collection) {
            for (Object obj : (Collection<?>) parameter) {
                processObject(obj);
            }
        } elseif (parameter instanceof Map) {
            for (Object value : ((Map<?, ?>) parameter).values()) {
                processObject(value);
            }
        } else {
            processObject(parameter);
        }
    }

    privatevoid decryptResult(Object result) {
        if (result == null) {
            return;
        }

        if (result instanceof List) {
            for (Object obj : (List<?>) result) {
                processObject(obj);
            }
        } else {
            processObject(result);
        }
    }

    privatevoid processObject(Object obj) {
        if (obj == null) {
            return;
        }

        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            if (field.isAnnotationPresent(Encrypt.class)) {
                field.setAccessible(true);
                try {
                    String value = (String) field.get(obj);
                    if (value != null) {
                        // 加密或解密操作
                        if (invocation.getMethod().getName().equals("update")) {
                            String encryptedValue = EncryptUtil.encrypt(value);
                            field.set(obj, encryptedValue);
                        } else {
                            String decryptedValue = EncryptUtil.decrypt(value);
                            field.set(obj, decryptedValue);
                        }
                    }
                } catch (IllegalAccessException e) {
                    thrownew RuntimeException("處理加密字段失敗", e);
                }
            }
        }
    }

    @Override
    publicObject plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    publicvoid setProperties(Properties properties) {
        // 可以在這里讀取配置文件中的參數(shù)
    }
}

這個(gè)攔截器的核心邏輯是:

  • 在 update 方法執(zhí)行前,對(duì)參數(shù)中的 Encrypt 注解標(biāo)記的字段進(jìn)行加密。
  • 在 query 方法執(zhí)行后,對(duì)結(jié)果中的 Encrypt 注解標(biāo)記的字段進(jìn)行解密。

3. 在 MyBatis 中注冊(cè)攔截器

同樣,在 MyBatis 的配置文件中注冊(cè)攔截器:

<plugins>
    <plugin interceptor="com.example.interceptor.EncryptInterceptor"/>
</plugins>

或者在 Spring Boot 中通過(guò) @Configuration 類(lèi)注冊(cè):

@Configuration
public class MyBatisConfig {

    @Bean
    public EncryptInterceptor encryptInterceptor() {
        return new EncryptInterceptor();
    }
}

4. 在實(shí)體類(lèi)中使用注解

現(xiàn)在,我們的實(shí)體類(lèi)可以這樣寫(xiě):

public class User {
    private Long id;
    privateString username;

    @Encrypt
    privateString phone;

    @Encrypt
    privateString idCard;

    @Encrypt
    privateString email;

    // 省略 getter 和 setter 方法
}

只需要在需要加密的字段上添加 @Encrypt 注解,攔截器就會(huì)自動(dòng)處理加密和解密,再也不用手動(dòng)添加 @TypeHandler 注解了!

四、MyBatis-Plus 的 “懶人模式”

如果你使用的是 MyBatis-Plus,那事情就更簡(jiǎn)單了。MyBatis-Plus 從 3.3.2 版本開(kāi)始,內(nèi)置了數(shù)據(jù)安全保護(hù)功能,支持字段加密和解密。

1. 添加依賴(lài)

首先,在 pom.xml 中添加 MyBatis-Plus 的依賴(lài):

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.10</version>
</dependency>

2. 配置加密

在 application.properties 中添加以下配置:

# 開(kāi)啟 MyBatis-Plus 的字段加密功能
mybatis-plus.global-config.encrypt.enable=true

# 配置加密密鑰(不要硬編碼,通過(guò)配置中心或環(huán)境變量獲?。?mybatis-plus.global-config.encrypt.key=mySecretKey123456

# 配置需要加密的字段
mybatis-plus.global-config.encrypt.fields=phone,idCard,email

這里的 fields 屬性指定了需要加密的字段,多個(gè)字段用逗號(hào)分隔。

3. 在實(shí)體類(lèi)中使用注解

@Data
@TableName("user")
publicclass User {
    private Long id;
    privateString username;

    @TableField(encrypt = true)
    privateString phone;

    @TableField(encrypt = true)
    privateString idCard;

    @TableField(encrypt = true)
    privateString email;
}

只需要在需要加密的字段上添加 @TableField(encrypt = true) 注解,MyBatis-Plus 就會(huì)自動(dòng)幫你處理加密和解密。

4. 進(jìn)階配置:自定義加密算法

如果你不滿(mǎn)足于默認(rèn)的 AES 算法,還可以自定義加密算法。首先,創(chuàng)建一個(gè)實(shí)現(xiàn) EncryptService 接口的類(lèi):

public class CustomEncryptService implements EncryptService {

    privatestatic final String KEY = "mySecretKey123456";

    @Override
    publicString encrypt(String value) {
        // 實(shí)現(xiàn)自定義加密邏輯
        return SM4.encrypt(value, KEY);
    }

    @Override
    publicString decrypt(String value) {
        // 實(shí)現(xiàn)自定義解密邏輯
        return SM4.decrypt(value, KEY);
    }
}

然后,在配置文件中指定使用自定義的加密服務(wù):

mybatis-plus.global-config.encrypt.service=com.example.service.CustomEncryptService

這樣,MyBatis-Plus 就會(huì)使用你自定義的加密算法了。

五、性能優(yōu)化:讓加密解密飛起來(lái)

雖然加密解密能保證數(shù)據(jù)的安全性,但它也會(huì)帶來(lái)一定的性能開(kāi)銷(xiāo)。尤其是在處理大量數(shù)據(jù)時(shí),這可能會(huì)成為系統(tǒng)的瓶頸。下面,我們就來(lái)看看如何優(yōu)化加密解密的性能。

1. 選擇合適的加密算法

不同的加密算法在性能上有很大差異。比如,AES 算法的速度比 RSA 算法快得多。如果你的項(xiàng)目對(duì)性能要求較高,建議選擇 AES 這樣的對(duì)稱(chēng)加密算法。如果需要非對(duì)稱(chēng)加密,可以考慮使用 RSA 來(lái)加密 AES 的密鑰,然后用 AES 來(lái)加密實(shí)際數(shù)據(jù),這樣可以在安全性和性能之間找到一個(gè)平衡點(diǎn)。

2. 緩存加密后的結(jié)果

如果某些數(shù)據(jù)在短時(shí)間內(nèi)會(huì)被頻繁訪(fǎng)問(wèn),你可以考慮將加密后的結(jié)果緩存起來(lái)。比如,使用 Redis 這樣的分布式緩存,將加密后的數(shù)據(jù)存儲(chǔ)在緩存中。這樣,當(dāng)再次訪(fǎng)問(wèn)這些數(shù)據(jù)時(shí),就可以直接從緩存中獲取,而不需要進(jìn)行解密操作。

3. 異步處理加密解密

如果加密解密的操作比較耗時(shí),你可以考慮將其放到異步線(xiàn)程中處理。比如,在插入數(shù)據(jù)時(shí),先將明文數(shù)據(jù)存入數(shù)據(jù)庫(kù),然后在異步線(xiàn)程中進(jìn)行加密,并更新數(shù)據(jù)庫(kù)中的數(shù)據(jù)。這樣,主業(yè)務(wù)線(xiàn)程就不會(huì)被加密操作阻塞,提高了系統(tǒng)的響應(yīng)速度。

4. 批量處理

如果需要處理大量的數(shù)據(jù),批量處理可以顯著提高性能。比如,在插入數(shù)據(jù)時(shí),將多個(gè)數(shù)據(jù)對(duì)象一次性加密,然后批量插入數(shù)據(jù)庫(kù)。這樣可以減少數(shù)據(jù)庫(kù)的交互次數(shù),提高吞吐量。

六、安全注意事項(xiàng):別讓你的加密 “裸奔”

雖然我們已經(jīng)實(shí)現(xiàn)了加密解密功能,但如果不注意安全細(xì)節(jié),你的系統(tǒng)可能還是漏洞百出。下面,我們就來(lái)看看需要注意的安全事項(xiàng)。

1. 密鑰管理

密鑰是加密解密的核心,一旦泄露,所有的加密數(shù)據(jù)都將形同虛設(shè)。因此,密鑰的管理至關(guān)重要:

  • 不要將密鑰硬編碼在代碼中,應(yīng)該通過(guò)配置文件、環(huán)境變量或配置中心來(lái)獲取。
  • 定期更換密鑰,防止長(zhǎng)期使用同一密鑰導(dǎo)致的安全風(fēng)險(xiǎn)。
  • 對(duì)密鑰進(jìn)行嚴(yán)格的訪(fǎng)問(wèn)控制,只有授權(quán)的人員才能訪(fǎng)問(wèn)密鑰。

2. 加密算法的選擇

不要使用已經(jīng)被證明不安全的加密算法,比如 MD5、SHA-1 等。建議選擇 AES、SM4 等安全的加密算法。如果你的項(xiàng)目涉及國(guó)家秘密,還需要使用國(guó)密算法,如 SM2、SM3、SM4。

3. 數(shù)據(jù)傳輸安全

加密解密不僅要考慮數(shù)據(jù)的存儲(chǔ)安全,還要考慮數(shù)據(jù)在傳輸過(guò)程中的安全。建議使用 HTTPS 來(lái)加密數(shù)據(jù)傳輸,防止中間人攻擊。

4. 日志安全

在日志中不要輸出加密后的明文數(shù)據(jù)。比如,在記錄用戶(hù)信息時(shí),應(yīng)該只記錄加密后的數(shù)據(jù),或者對(duì)敏感字段進(jìn)行脫敏處理。否則,日志文件可能會(huì)成為黑客獲取敏感信息的突破口。

七、總結(jié):擁抱自動(dòng)化,告別手動(dòng)搬磚

通過(guò) MyBatis 的自定義類(lèi)型處理器、攔截器和 MyBatis-Plus 的內(nèi)置功能,我們可以輕松實(shí)現(xiàn)數(shù)據(jù)的加密解密,徹底扔掉那個(gè)笨重的工具類(lèi)。這種自動(dòng)化的處理方式不僅減少了代碼冗余,提高了開(kāi)發(fā)效率,還增強(qiáng)了系統(tǒng)的安全性和可維護(hù)性。

當(dāng)然,加密解密只是數(shù)據(jù)安全的一部分。在實(shí)際項(xiàng)目中,還需要結(jié)合訪(fǎng)問(wèn)控制、數(shù)據(jù)脫敏、安全審計(jì)等多種手段,構(gòu)建全方位的數(shù)據(jù)安全防護(hù)體系。

責(zé)任編輯:武曉燕 來(lái)源: 石杉的架構(gòu)筆記
相關(guān)推薦

2022-03-24 09:13:54

Mybatis加密解密

2022-09-14 10:16:12

MyBatis加密解密

2011-04-12 14:58:23

加密解密類(lèi)

2018-08-29 14:50:15

文件加密

2011-03-24 09:34:41

SPRING

2022-11-08 15:14:17

MyBatis插件

2024-03-01 09:58:44

2013-01-14 15:41:27

Android模擬器教程配置

2022-06-23 08:42:08

配置加密解密

2022-05-26 10:42:30

數(shù)據(jù)權(quán)限注解

2012-07-17 17:05:55

JavaScript

2011-06-17 16:49:05

Cocoa蘋(píng)果

2009-07-07 11:38:54

jsp oracle

2020-07-20 10:00:52

Python翻譯工具命令行

2022-01-26 15:20:00

配置微服務(wù)架構(gòu)

2013-12-10 22:35:56

CloudStackIaaS自動(dòng)化

2022-05-20 12:24:45

分庫(kù)分表Java依賴(lài)

2021-10-08 19:00:28

NMState網(wǎng)絡(luò)配置工具系統(tǒng)運(yùn)維

2022-02-17 18:21:47

工具HTTPie客戶(hù)端

2020-12-08 06:23:05

LockSupport線(xiàn)程工具
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)