IBatis/IBatis.Net中的数据加密
作为一个o/r mapping组件,ibatis通过反射(reflect)完成对象的持久化,
如果要对数据进行加密,那么切入点就在reflect上,
思路就是当ibatis通过reflect给对象属性赋值时,我们解密数据,反之加密数据。
那么如何切入了?先跟踪源代码看看 :)
如何跟踪源代码这里就不繁述了,这里列出几个重要的对象/接口:
1. dotnetobjectdataexchange:顾名思义,对象数据交换类;
2. accessorfactory:存取器工厂类;
3. isetaccessorfactory/igetaccessorfactory:set/get存取器工厂,对应属性的set/get操作;
4. isetaccessor/igetaccessor:set/get存取器;
显然上面几个对象/接口都是可切入的点,例如:
1. 用一个secureobjectdataexchange去替换dotnetobjectdataexchange;
2. 实现自己的setaccessorfactory/getaccessorfactory对象;
切入点有了,那如何让ibatis调用了?也即如何注入到ibatis runtime中呢?
我们知道ibatis对外的接口是isqlmapper,而sqlmapper由domsqlmapbuilder创建,
在domsqlmapbuilder有两个属性就是我们需要的:
// allow a custom isetaccessorfactory to be set before configuration.
public isetaccessorfactory setaccessorfactory;
// allow a custom igetaccessorfactory to be set before configuration.
public igetaccessorfactory getaccessorfactory;
ok,现在万事俱备了,开始写代码:
step 1:
定制igetaccessorfactory / isetaccessorfactory;
public class securegetaccessorfactory : igetaccessorfactory
{
public igetaccessor creategetaccessor(type targettype, string name)
{
return new securegetaccessor(targettype, name);
}
}
public class securesetaccessorfactory : isetaccessorfactory
{
public isetaccessor createsetaccessor(type targettype, string name)
{
return new securesetaccessor(targettype, name);
}
}
step 2:
定制igetaccessor / isetaccessor
public class securegetaccessor : igetaccessor
{
private propertyinfo _propertyinfo = null;
private string _propertyname = string.empty;
private type _targettype = null;
private bool _isprotected = false;
public securegetaccessor(type targettype, string propertyname)
{
reflectioninfo reflectioncache = reflectioninfo.getinstance( targettype );
_propertyinfo = (propertyinfo)reflectioncache.getgetter(propertyname);
object[] attrs = _propertyinfo.getcustomattributes(typeof(protectfieldattribute), true);
_isprotected = attrs.length > 0;
_targettype = targettype;
_propertyname = propertyname;
}
#region iaccessor members
public string name
{
get { return _propertyinfo.name; }
}
public type membertype
{
get { return _propertyinfo.propertytype; }
}
#endregion
#region iget members
public object get(object target)
{
if (_propertyinfo.canread)
{
object ret = _propertyinfo.getvalue(target, null);
if (_isprotected)
{
// 加密
ret = secureutil.encryptdata(ret);
}
return ret;
}
else
{
throw new notsupportedexception(
string.format("property ""{0}"" on type "
+ "{1} doesn't have a get method.", _propertyname, _targettype));
}
}
#endregion
}
public class securesetaccessor : isetaccessor
{
private propertyinfo _propertyinfo = null;
private string _propertyname = string.empty;
private type _targettype = null;
private bool _isprotected = false;
public securesetaccessor(type targettype, string propertyname)
{
reflectioninfo reflectioncache = reflectioninfo.getinstance( targettype );
_propertyinfo = (propertyinfo)reflectioncache.getgetter(propertyname);
object[] attrs = _propertyinfo.getcustomattributes(typeof(protectfieldattribute), true);
_isprotected = attrs.length > 0;
_targettype = targettype;
_propertyname = propertyname;
}
#region iaccessor members
public string name
{
get { return _propertyinfo.name; }
}
public type membertype
{
get { return _propertyinfo.propertytype; }
}
#endregion
#region iget members
public void set(object target, object value)
{
if (_propertyinfo.canwrite)
{
if (_isprotected)
{
// 解密
value = secureutil.decryptdata(value);
}
_propertyinfo.setvalue(target, value, null);
}
else
{
throw new notsupportedexception(
string.format("property ""{0}"" on type "
+ "{1} doesn't have a set method.", _propertyname, _targettype));
}
}
#endregion
}
step 3:
注入ibatis runtime.
domsqlmapbuilder builder = new domsqlmapbuilder();
builder.setaccessorfactory = new securesetaccessorfactory();
builder.getaccessorfactory = new securegetaccessorfactory();
isqlmapper mapper = builder.configure("./sqlmap.config");
注:
protectfieldattribute为一个attribute,用于标明那些字段需要保护,
secureutil为一个加解密的对象,大家可用自己的加解密方法替代。
总结:
通过定制igetaccessorfactory/isetaccessorfactory接口,我们很容易就切入到对象持久化的流程中,
本文只是给出了一个数据加密的例子,相信大家能找到更多可应用的场合。
对于本文案例,还有一个缺点,就是再对数据进行加解密时,必须保证它的类型不变,
这就限制我们只能对字符串进行加解密,当然如果能将int加密后还是int,那也是能用的。
另一个问题就是数据加密后,显然就没法查找了,这只能通过其它方式来弥补了,如使用lucene.
原文地址: http://blog.csdn.net/billy_zh/archive/2008/09/04/2880739.aspx