mybatis類型轉(zhuǎn)換器如何實(shí)現(xiàn)數(shù)據(jù)加解密
mybatis類型轉(zhuǎn)換器數(shù)據(jù)加解密
背景需求
對表中的某些字段進(jìn)行加密,查詢之后對字段進(jìn)行解密,業(yè)務(wù)代碼無需關(guān)注加解密環(huán)節(jié)。
mybatis 攔截器 vs 類型轉(zhuǎn)換器
mybatis的攔截器能實(shí)現(xiàn)上面的需求,但是會(huì)攔截所有的sql語句,如果項(xiàng)目中只是部分sql涉及到加解密操作,還是比較犧牲大局的。實(shí)現(xiàn)起來也比較麻煩,特別是單參數(shù)查詢的時(shí)候,比如參數(shù)名稱name,此時(shí)需要對name進(jìn)行加密就相對困難。如果是多參數(shù),因?yàn)榈讓訒?huì)將參數(shù)封裝成Map,可以根據(jù)是否含有指定的key進(jìn)行加密。
mybatis類型轉(zhuǎn)換器也可以實(shí)現(xiàn)上面的需求,方案為修改xml里面的配置,指定類型轉(zhuǎn)換器,并在轉(zhuǎn)換器里面實(shí)現(xiàn)數(shù)據(jù)的加解密。
mybatis 類型轉(zhuǎn)換器
java有java的數(shù)據(jù)類型,數(shù)據(jù)庫有數(shù)據(jù)庫的數(shù)據(jù)類型,那么我們在往數(shù)據(jù)庫中插入數(shù)據(jù)的時(shí)候是如何把java類型當(dāng)做數(shù)據(jù)庫類型插入數(shù)據(jù)庫,在從數(shù)據(jù)庫讀取數(shù)據(jù)的時(shí)候又是如何把數(shù)據(jù)庫類型當(dāng)做java類型來處理呢?這中間必然要經(jīng)過一個(gè)類型轉(zhuǎn)換。
mybatis提供了TypeHandler接口,抽象類BaseTypeHandler實(shí)現(xiàn)TypeHandler接口,String,Integer,Long等常見的數(shù)據(jù)類型轉(zhuǎn)換都是基于繼承BaseTypeHandler實(shí)現(xiàn),BaseTypeHandler提供了基本的判空等操作。因此我們可以繼承BaseTypeHandler或者實(shí)現(xiàn)TypeHandler接口完成自定義類型轉(zhuǎn)換,一般建議繼承BaseTypeHandler來編碼。
數(shù)據(jù)加解密類型轉(zhuǎn)換
假設(shè)加解密采用AES算法,此處對加解密結(jié)果做一下緩存,因?yàn)橐恍?shù)據(jù)是一樣的,如果每次都去加密,也是會(huì)消耗一定的時(shí)間
public class DataSecurityHandler extends BaseTypeHandler { public static final LoadingCache<String, String> ENCRYPT_CACHE = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(new CacheLoader<String, String>() { public String load(String parameterValue) { return AES.encrypt(parameterValue); } }); public static final LoadingCache<String, String> DECRYPT_CACHE = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(new CacheLoader<String, String>() { public String load(String parameterValue) { return AES.decrypt(parameterValue); } }); @Override public void setNonNullParameter(PreparedStatement preparedStatement, int i, Object parameter, JdbcType jdbcType) throws SQLException { String parameterValue = (String) parameter; String resultValue = ENCRYPT_CACHE.getUnchecked(StringUtils.trimToEmpty(parameterValue)); preparedStatement.setString(i, resultValue); } @Override public Object getNullableResult(ResultSet resultSet, String s) throws SQLException { return DECRYPT_CACHE.getUnchecked(StringUtils.trimToEmpty(resultSet.getString(s))); } @Override public Object getNullableResult(ResultSet resultSet, int i) throws SQLException { return DECRYPT_CACHE.getUnchecked(StringUtils.trimToEmpty(resultSet.getString(i))); } @Override public Object getNullableResult(CallableStatement callableStatement, int i) throws SQLException { return DECRYPT_CACHE.getUnchecked(StringUtils.trimToEmpty(callableStatement.getString(i))); } }
使用方法
針對sql語句,在xml配置文件中,對需要加密的字段加上
#{name,jdbcType=VARCHAR,typeHandler=com..........DataSecurityHandler},
針對查詢結(jié)果,在xml配置文件中,在resultMap里面對需要解密的字段加上
<result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.*..........DataSecurityHandler"/>
mybatis密碼解密
mybatis的配置
properties的配置
代碼:
package com.finance.entity.batctrl; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.Properties; import javax.sql.DataSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.codec.binary.Base64; import org.apache.ibatis.datasource.pooled.PooledDataSource; import org.apache.ibatis.io.Resources; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.ibatis.transaction.TransactionFactory; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class MyBatisSessionFactory { /** * session工廠 */ private final static SqlSessionFactory sqlSessionFactory; /** * 日志類 */ private static Logger logger = LoggerFactory.getLogger(MyBatisSessionFactory.class); static { String resource = "mybatis-configuration.xml"; //Reader reader = null; InputStream inputStream = null; Properties props = new Properties(); try { //reader = Resources.getResourceAsReader(resource); InputStream in = Resources.getResourceAsStream("batch.properties"); props.load(in); String url= props.getProperty("jdbc.url"); String username= props.getProperty("jdbc.username"); String password= props.getProperty("jdbc.password"); logger.debug("befor url -> "+url); logger.debug("befor username -> "+username); logger.debug("befor decode -> "+password); byte[] b = Base64.decodeBase64(password.getBytes()); String decode = new String(b); props.put("url",url); props.put("username",username); props.put("password",decode); logger.debug("after url -> "+url); logger.debug("after username -> "+username); logger.debug("after decode -> "+decode); inputStream = Resources.getResourceAsStream(resource); } catch (IOException e) { } //sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream ,props ); } public static SqlSessionFactory getSessionFactory() { return sqlSessionFactory; } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot多數(shù)據(jù)庫連接(mysql+oracle)的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot多數(shù)據(jù)庫連接(mysql+oracle)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03關(guān)于Java中如何實(shí)現(xiàn)文件的讀寫操作
在Java中,可以使用File和FileInputStream、FileOutputStream、BufferedReader、PrintWriter等類來進(jìn)行文件讀寫操作,需要的朋友可以參考下2023-05-05java實(shí)現(xiàn)開根號的運(yùn)算方式
這篇文章主要介紹了java實(shí)現(xiàn)開根號的運(yùn)算方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07java模擬ajax訪問另一個(gè)項(xiàng)目的controller代碼實(shí)例
今天小編就為大家分享一篇關(guān)于java模擬ajax訪問另一個(gè)項(xiàng)目的controller代碼實(shí)例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03SpringMVC?bean實(shí)現(xiàn)加載控制方法詳解
SpringMVC是一種基于Java,實(shí)現(xiàn)了Web?MVC設(shè)計(jì)模式,請求驅(qū)動(dòng)類型的輕量級Web框架,即使用了MVC架構(gòu)模式的思想,將Web層進(jìn)行職責(zé)解耦?;谡埱篁?qū)動(dòng)指的就是使用請求-響應(yīng)模型,框架的目的就是幫助我們簡化開發(fā),SpringMVC也是要簡化我們?nèi)粘eb開發(fā)2022-08-08springboot中使用Hibernate-Validation校驗(yàn)參數(shù)詳解
這篇文章主要為大家介紹了springboot中使用Hibernate-Validation校驗(yàn)參數(shù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Java判斷IP地址為內(nèi)網(wǎng)IP還是公網(wǎng)IP的方法
這篇文章主要介紹了Java判斷IP地址為內(nèi)網(wǎng)IP還是公網(wǎng)IP的方法,針對tcp/ip協(xié)議中保留的三個(gè)私有地址進(jìn)行判斷分析,是比較實(shí)用的技巧,需要的朋友可以參考下2015-01-01