我们正在使用WebAPI / OData项目为我们的应用程序公开数据层。
.NET Framework 4.6.1
System.Web.OData,Version = 5.9.0.0
我们为各种数据集(外科医生,患者等)提供了几个OData控制器。如果我正在尝试搜索患者,我会发出类似以下内容的OData查询,以获得符合我所需标准的患者列表。
http://localhost/MyService/Patients?$filter=contains($it/Name,”Joe”)
问题是我可能不被允许查看所有患者。由于RESTful API将在外部公开,因此我无法通过操纵URL来依赖客户端来强制执行安全性要求。
如果我使用声明授予用户访问特定患者的权限,我可以切实保护对单个用户的访问权限,但搜索匹配用户列表根本不起作用。
http://localhost/MyService/Patients(Id)
如果我尝试解决这个问题并通过拥有患者的实体进行访问,则会在下一个更高级别发生相同类型的问题。换句话说,如果外科医生拥有患者,那么我如何获得我可以访问的外科医生名单?
有没有办法为OData构建的查询注入一些额外的标准来强制执行访问控制?
答案 0 :(得分:1)
这是患者OData控制器的GET方法
public class PatientsController : BaseODataController<Patient>
{
public override IQueryable<Patient> Get()
{
return base.Get();
}
...
}
它返回 IQueryable&lt; Patient&gt; ...
添加访问控制过滤我们唯一需要做的就是添加其他谓词...例如:
public override IQueryable<Patient> Get()
{
var qry = base.Get();
return qry.Where(itm => itm.Name.FirstName.Contains("R"));
}
当控制器基础结构枚举 IQueryable&lt; T&gt; 时,它会评估整个工作单元并生成最终查询。
如果我对数据库运行跟踪并为其提供更复杂的OData查询,如
http://localhost/MyService/Patients?$filter=Surgeries/any(d:d/PreOpDataComplete%20eq%20true).
(换句话说,找到PreOpDataComplete标志为true的Surgeries)我将看到执行以下查询。
exec sp_executesql N'SELECT
[Project1].[C1] AS [C1],
[Project1].[Id] AS [Id],
[Project1].[Name_FirstName] AS [Name_FirstName],
[Project1].[Name_LastName] AS [Name_LastName],
[Project1].[Gender] AS [Gender],
[Project1].[BirthDate] AS [BirthDate],
[Project1].[MedicalRecordId] AS [MedicalRecordId],
[Project1].[Surgeon_Id] AS [Surgeon_Id]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name_FirstName] AS [Name_FirstName],
[Extent1].[Name_LastName] AS [Name_LastName],
[Extent1].[Gender] AS [Gender],
[Extent1].[BirthDate] AS [BirthDate],
[Extent1].[MedicalRecordId] AS [MedicalRecordId],
[Extent1].[Surgeon_Id] AS [Surgeon_Id],
1 AS [C1]
FROM [dbo].[Patient] AS [Extent1]
WHERE [Extent1].[Name_FirstName] LIKE N''%R%''
) AS [Project1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Surgery] AS [Extent2]
WHERE ([Project1].[Id] = [Extent2].[Patient_Id]) AND ([Extent2].[PreOpDataComplete] = @p__linq__0)
)',N'@p__linq__0 bit',@p__linq__0=1
我得到了预期的结果
{
"@odata.context":"http://localhost/MyService/$metadata#Patients",
"value":[{
"Name":{
"FirstName":"Ronn",
"LastName":"Black"
},
"Gender":"M",
"BirthDate":"1917-02-02T00:00:00-08:00",
"MedicalRecordId":"MRN 0001",
"Id":"8bf6dcc4-3f00-4a40-980c-ceb13f8f5360"
}]
}
如果我假设一个简单的安全模型,其中外科医生拥有他们自己的患者,并且我有一个外科医生ID列表,我可以在索赔中访问。我会有一个看起来像这样的外科医生实体:
public partial class Surgeon : IBaseEntity
{
[Key]
public Guid Id { get; set; }
public virtual ICollection<Patient> Patients { get; set; }
. . .
}
现在,如果我进行以下修改,我可以将所有搜索限制在我允许的患者身上:
public override IQueryable<Patient> Get()
{
//IQueriable from the OData Selection
var qry = base.Get();
//Enforce Access Security
var accessList = {get list of authorized surgeon id's from claims};
var finalQry = uow.Surgeons
.Where(s => accessList.Contains(s.Id)) //Restrict to Surgeons I'm allowed to see
.SelectMany(surgeon => surgeon.Patients) //All the patients I'm allowed to see (Left)
.Join(qry, //Join to Query (Right)
allowedPatients => allowedPatients.Id, //Key for Left
qryPatients => qryPatients.Id, //Key for Right
(allowedPatients, qryPatients) => //Iterate through Matches
qryPatients); //Return the Matches from Right
return finalQry;
}