IBatis.Net中的数据加密

当IBatis通过Reflect给对象属性赋值时,我们解密数据,反之加密数据。

作为一个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,那也是能用的。

另一个问题就是数据加密后,显然就没法查找了,这只能通过其它方式来弥补了,如使用LuceneNet.

文章出处:JPCode.net 精品代码网
链接:http://www.jpcode.net/article/10038

标签: iBatis


评论: