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

posted on 2009-01-19 20:13  arnold zhang  阅读(411)  评论(0编辑  收藏  举报