基于角色的数据契约代理实现

在设计面向不同角色用户需求的通用服务层和数据契约时,用户角色的区分显得尤为重要。因为某些敏感数据点只能与具有特权的用户共享。一种设计方法是使数据转换器(translators)具有角色特定性,这样转换器可以根据用户角色序列化数据。但这种方法简单却不优雅,会在各种转换器中引入大量的额外角色检查。

另一种方法是保持服务层和转换器的角色不可知性,并通过使用数据契约代理(Data Contract Surrogates)的声明式方法来实现。数据契约代理是一个高级特性,可以在数据契约序列化/反序列化过程中引入特殊行为。

实现步骤

首先,定义一个名为ConditionalDataMemberAttribute的属性,该属性将用于装饰需要条件性序列化的DataMember。

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public class ConditionalDataMemberAttribute : Attribute { }

接下来,创建一个名为ConditionalDataBehavior的自定义服务行为。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class ConditionalDataBehavior : Attribute, IServiceBehavior { }

在服务行为ConditionalDataBehavior中,重写每个操作的DataContractSurrogate,使用自定义实现的ConditionalDataContractSurrogate

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (ServiceEndpoint ep in serviceHostBase.Description.Endpoints) { foreach (OperationDescription od in ep.Contract.Operations) { ApplyDataContractSurrogate(od); } } }

ConditionalDataContractSurrogate类实现了IDataContractSurrogate接口。在GetObjectToSerialize方法中添加自定义实现。在序列化过程中,此方法将被调用以检查每个需要序列化的对象。如果调用上下文未授权接收对象,则阻止实际值被序列化。

public object GetObjectToSerialize(object obj, Type targetType) { if (obj == null) return null; var type = obj.GetType(); type.GetProperties().ToList().ForEach(prop => { try { var attr = prop.GetCustomAttributes(typeof(ConditionalDataMemberAttribute), false); if (attr.Any()) { var role = ((ConditionalDataMemberAttribute)attr[0]).Role; if (!IsAuthorized(role)) { var proptype = prop.PropertyType; prop.GetSetMethod().Invoke(obj, new[] { proptype.IsValueType ? Activator.CreateInstance(proptype) : null }); } } } catch { } }); return _baseSerializer != null ? _baseSerializer.GetObjectToSerialize(obj, targetType) : obj; }

步骤1:使用ConditionalDataBehavior属性装饰WebService。

[ServiceBehavior(Namespace = "http://mywebservice.com/v1")] [ConditionalDataBehavior] public class MyWebService { }

步骤2:使用ConditionalDataMember属性装饰数据契约。

[DataContract] public class UserInformation { [DataMember] public string FirstName { get; set; } [DataMember] public string LastName { get; set; } [DataMember] [ConditionalDataMember(Role = "Level2")] public string Email { get; set; } [DataMember] [ConditionalDataMember(Role = "Level2")] public string Phone { get; set; } }

步骤3:添加自己的IsAuthorized函数实现。

private bool IsAuthorized(string role) { // 实现自己的授权检查 currentUser.Roles.HasDesiredRole return true; }

示例输出

对象由转换器返回:

new UserInformation { FirstName = "Joe", LastName = "Doe", Email = "joe@doe.com", PhoneNumber = "408000000" }; Joe Doe joe@doe.com 408000000 Joe Doe
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485