如何给数字Id在Json响应中进行加密;

给大家说说我们公司的一个稀奇的需求吧

数据库设计时,只给自己的内部公司使用,所以里面的所有实体都是使用的 int 类型的 Id ;
后来,突然要求接入外部公司了,老板于是要求所有对外的 Id 均不可使用数字 Id; 免得什么什么什么的 (涉及大约 200 个接口),悲催的是数据库里面一堆的关联,要改也不好改。。

好消息是对外的接口暂时未发布,所以不存在对外的兼容性问题;
解决方案1. 
修改实体定义,

public int PartnerId { get; set; }    ===> 

[Column(TypeName = "int")]  
public IdAndXid  PartnerId { get; set; }       

改为另外一个类; 这个类中 Id 为 JsonIgnor,     Xid 则利用一个稀奇古怪的加解密算法计算为加密后的Id字符串;  
貌似一切正常,就这么办。。

 但是好景不长,过了一会儿,
发现这样的定义了, 
public int? PartnerId { get; set; }    ===> 

[Column(TypeName = "int")]  
public IdAndXid  PartnerId { get; set; }       

这儿 EF 非常坚定的要求 PartnerId 必须不为 null, 貌似解决不了;

于是尝试另外一个解决方法:
解决方法2。 (Json序列化和反序列化,我们使用的是Json.Net)

1. 定义 CryptIdAttribute, 然后在需要加密的 Property 上增加此 Attribute

    /// <summary>
    /// 加密的Id标注
    /// </summary>
    public class CryptIdAttribute : Attribute
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public CryptIdAttribute() { }
        
        /// <summary>
        /// 加密的类型
        /// </summary>
        public Type Type { get; set; }
    }


2. 再定义一个   Newtonsoft.Json.Serialization.DefaultContractResolver 的子类,
   覆盖其 CreateProperties 方法,
   将标记了  CryptIdAttribute  了的 Property  
   单独使用自定义的 CryptIdJsonProperty 进行处理;
   注意,我这儿增加另外的一个处理,即对于  TypeSciptIgnore 标记的属性,也不进行Json序列化,和本文无关;

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace hongbao.Json
{
    /// <summary>
    /// 输出到json字符串时,包含有TypeScriptIgnore标记的属性不进行序列化
    /// </summary>
    public class IgnoreTypeScriptResolver : DefaultContractResolver
    {
        /// <summary>
        /// TypeScriptIgnoreAttribute 标记类;
        /// </summary>
        static Type TypeSciptIgnoreType = typeof(TypeScriptIgnoreAttribute);

        /// <summary>
        /// TypeScriptIgnoreAttribute 标记类;
        /// </summary>
        static Type CryptIdType = typeof(CryptIdAttribute);

        [ThreadStatic]
        PropertyInfo[] ignoreProperty;

        [ThreadStatic]
        Tuple<PropertyInfo,CryptIdAttribute>[] cryptIdProperty;

        /// <summary>
        /// Json序列化时,将传入 type, 以解决数据协议; 
        /// 此处查询所有包含有 TypeSciptIgnoreType 标注 的属性出来;
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public override JsonContract ResolveContract(Type type)
        {
             //cryptIdProperty = new Tuple<PropertyInfo, CryptIdAttribute>[] { }; 
             cryptIdProperty = type.GetProperties() //  异常。。。Newtonsoft中抛出
                 .Select(a => new { Prop = a, Attr = a.GetCustomAttributes(CryptIdType, true).FirstOrDefault() as CryptIdAttribute })
                 .Where(b => b.Attr != null)
                 .Select(c => Tuple.Create(c.Prop, c.Attr))
                 .ToArray();

            ignoreProperty = type.GetProperties().Where(a => a.GetCustomAttributes(TypeSciptIgnoreType, true).Length > 0).ToArray();
            return base.ResolveContract(type);
        }
       

        /// <summary>
        /// Json序列化时,将传入 type 以获取需要序列化的属性;
        /// 获取需要序列化的属性, 但是忽略掉 包含有  TypeSciptIgnoreType 标注 的属性
        /// </summary>
        /// <param name="type"></param>
        /// <param name="memberSerialization"></param>
        /// <returns></returns>
        protected override IList<JsonProperty> CreateProperties(Type type,
                MemberSerialization memberSerialization)
        {
            IList<JsonProperty> list = base.CreateProperties(type, memberSerialization);            
            if (ignoreProperty == null) return list;
            var result = list.Where(jsonProperty => !ignoreProperty.Any(c => c.Name == jsonProperty.PropertyName) && 
                    !cryptIdProperty.Any(a=> a.Item1.Name == jsonProperty.PropertyName)
            )
                .Union(cryptIdProperty.Select(a => new CryptIdJsonProperty(a.Item1, a.Item2.Type)))
                .ToList();
            return result;
        }
    }
}


3.  CryptIdJsonProperty  大致构造函数如下:

/// <summary>
    /// 对于 CryptIdAttribute 标记的属性,使用 CryptIdJsonPropertyp 进行进行处理
    /// </summary>
    public class CryptIdJsonProperty : JsonProperty
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="property"></param>
        /// <param name="cryptType"></param>
        public CryptIdJsonProperty(PropertyInfo property, Type cryptType)
        {
            this.PropertyName = property.Name;
            this.ValueProvider = new ReflectionValueProvider(property); 
            this.Converter = new CryptIdConvert(false, cryptType);  //必须提供;
            this.Writable = true;
            this.Readable = true;
            this.PropertyType = property.PropertyType;
            
            this.ShouldSerialize = new Predicate<object>((xx) => true);
            this.GetIsSpecified = null; // new Predicate<object>((xx) => true);
            this.SetIsSpecified = null; // new Predicate<object, object>((xx, yy) => true);
        }
    }

4. 再定义一个 CryptIdConvert 类继承       JsonConverter , 
 

 /// <summary>
    /// CryptIdAttribute标记的属性,使用 CryptIdConvert 进行转换
    /// </summary>
    public class CryptIdConvert : JsonConverter
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="allowNull"></param>
        /// <param name="cryptType"></param>
        public CryptIdConvert(bool allowNull, Type cryptType)
        {
            this.allowNull = allowNull;
            this.cryptType = cryptType;
        }

        private Type cryptType;
        private bool allowNull = false;

        /// <summary>
        /// 能否转换
        /// </summary>
        /// <param name="objectType">对象的类型;</param>
        public override bool CanConvert(Type objectType)
        {
            return true;
        }

        /// <summary>
        /// 从字符串转换会 int? 或者 int, 解密成id
        /// </summary>
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var value = (string)reader.Value;
            if (value == null || value == "") return null;
            return SecurityUtil.DecryptIdInGuid(value, objectType.Name);
        }

        /// <summary>
        /// 从字符串转换会 int? 或者 int, 加密成id
        /// </summary>
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value == null) { writer.WriteValue(value); return; }
            string val = "";
            if (allowNull)
            {
                var id = (int?)(value);
                if (id.HasValue) val = SecurityUtil.CryptIdInGuid(id, cryptType);
                else
                {
                    writer.WriteValue((object)null);
                    return;
                }
            }
            else
            {
                var id = (int)(value);
                val = SecurityUtil.CryptIdInGuid(id, cryptType);
            }
            writer.WriteValue(val);
        }
    }

5.  定义Json序列化方法 , 一般是在你自己的  JsonResult 的子类中覆盖其 ExecuteResult 方法;
         

#region 实现JsonResult的输出方法
        /// <summary>
        /// 覆盖 JsonResult 的输出方法; 
        /// </summary>
        /// <param name="context"></param>
        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if ((this.JsonRequestBehavior == JsonRequestBehavior.DenyGet)
                && string.Equals(context.HttpContext.Request.HttpMethod,
                    "GET", StringComparison.OrdinalIgnoreCase))
            {
                throw new InvalidOperationException("不允许进行GET操作");
            }
            HttpResponseBase response = context.HttpContext.Response;
            if (!string.IsNullOrEmpty(this.ContentType))
            {
                response.ContentType = this.ContentType;
            }
            else
            {
                response.ContentType = "application/json";
            }
            if (this.ContentEncoding != null)
            {
                response.ContentEncoding = this.ContentEncoding;
            }

            var obj = new
            {
                code,
                message,
                data = this.Data,
                token
            };
            IsoDateTimeConverter timeConvert = new IsoDateTimeConverter()
            {
                DateTimeFormat = "yyyy-MM-dd HH:mm:ss"
            };
            var JsonSetting = new JsonSerializerSettings
            {
                Converters = new JsonConverter[] { timeConvert },
                ContractResolver = new IgnoreTypeScriptResolver(), //不输出 IgnoreTypeScript的标记属性;
            };
            string jsonContent = JsonConvert.SerializeObject(obj, Formatting.None, JsonSetting);
            response.Write(jsonContent);
        }


        #endregion

不得不说, Json.Net 非常灵活;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值