在设计面向不同角色用户需求的通用服务层和数据契约时,用户角色的区分显得尤为重要。因为某些敏感数据点只能与具有特权的用户共享。一种设计方法是使数据转换器(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