<script type="text/javascript" src="http://www.cchensoft.com/gad/blog_csdn_article.js"></script>
作为一个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.
<script type="text/javascript" src="http://www.cchensoft.com/gad/blog_csdn_article.js"></script>