介绍 OData Web API 授权库
我想介绍一下用于 Web API 的 OData 授权库。使用 OData 模型构建器,您可以使用权限限制对 EDM 模型进行注释,以告知 API 哪些操作需要哪些权限。这些批注基于 OData 功能词汇表。但是,没有任何内容会在您的 API 上强制执行这些权限。您需要在应用程序中手动定义授权策略才能应用这些权限。使用新的授权库,只需一些设置代码,即可将这些权限自动应用于 OData API 终结点。
在本教程中,我将向您展示如何使用该库向简单的 OData API 添加授权。
该库目前在 NuGet 上以测试版本的形式提供,作为微软.AspNetCore.OData.授权,它目前仅支持基于具有终结点路由的 AspNetCore 3.1 的 OData WebApi 7.x 应用程序。
创建应用程序
- 使用 API 模板创建一个 ASP.NET 核心 3.1 Web 应用程序。我们调用应用程序 OData授权演示
- 安装以下 NuGet 包:
Microsoft.AspNetCore.OData(7.5.Microsoft.EntityFrameworkCore(我们将使用 EF 核心与数据库进行交互)Microsoft.EntityFrameworkCore.InMemory(对于此演示,我们将使用内存中数据库)Microsoft.OData.ModelBuilder(1.0.3 或更高版本)(我们将使用 创建 OData 模型并指定权限限制)Microsoft.AspNetCore.OData.Authorization(0.1.0-测试版)(WebApi 授权库,请参阅以下说明)
安装授权包时,请像往常一样在安装包时转到“管理 NuGet 包”。请务必选中包括预发行版复选框。您现在应该能够搜索 .Microsoft.AspNetCore.OData.Authorization
创建数据库上下文和模型类
出于演示目的,我们将仅创建一个实体:。Product
创建一个名为 的文件夹。将以下类文件添加到该文件夹并调用它:ModelsProduct.cs
using System.ComponentModel.DataAnnotations;
namespace ODataAuthorizationDemo.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public int Price { get; set; }
}
}
接下来,让我们创建 EF 核心数据库上下文。使用以下代码将文件添加到该文件夹:AppDbContext.csModels
using Microsoft.EntityFrameworkCore;
namespace ODataAuthorizationDemo.Models
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Product> Products { get; set; }
}
}
创建 OData 模型
对于此演示,我们将使用 OData 模型构建器包基于 C# 模型类创建 OData 模型。我们还将使用模型生成器添加权限限制。
让我们使用以下代码在文件夹中添加 一个:AppEdmModel.csModels
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
namespace ODataAuthorizationDemo.Models
{
public static class AppEdmModel
{
public static IEdmModel GetModel()
{
var builder = new ODataConventionModelBuilder();
var products = builder.EntitySet<Product>("Products");
products.HasReadRestrictions()
.HasPermissions(p =>
p.HasSchemeName("Scheme").HasScopes(s => s.HasScope("Product.Read")))
.HasReadByKeyRestrictions(r => r.HasPermissions(p =>
p.HasSchemeName("Scheme").HasScopes(s => s.HasScope("Product.ReadByKey"))));
products.HasInsertRestrictions()
.HasPermissions(p => p.HasSchemeName("Scheme").HasScopes(s => s.HasScope("Product.Create")));
products.HasUpdateRestrictions()
.HasPermissions(p => p.HasSchemeName("Scheme").HasScopes(s => s.HasScope("Product.Update")));
products.HasDeleteRestrictions()
.HasPermissions(p => p.HasSchemeName("Scheme").HasScopes(s => s.HasScope("Product.Delete")));
return builder.GetEdmModel();
}
}
}
这将创建一个模型,其中包含基于实体类型的实体集。它为该实体集上的不同 CRUD 操作添加权限限制,指定执行这些操作所需的作用域。ProductsProduct
- 阅读产品()要求用户具有范围
GET /ProductsProduct.Read - 通过密钥 () 读取单个产品也可以使用范围进行访问,以防用户没有范围。
GET /Products(1)Product.ReadByKeyProducts.Read - 创建新产品 () 需要范围
POST /Products(1)Product.Create - 更新产品 () 需要
PATCH /Products(1)Product.Update - 删除产品( 需要
DELETE /Products(1)Product.Delete)
这些限制将作为功能批注添加到 OData 模型中。授权中间件将读取这些注释,以提取不同请求所需的权限范围。默认情况下,将允许未定义任何限制的操作。
配置启动服务
现在,让我们在文件中配置不同的服务和应用程序构建器。Startup.cs
让我们修改该方法,使其如下所示:ConfgiureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(opt => opt.UseInMemoryDatabase("ODataAuthDemo"));
services.AddOData();
services.AddRouting();
}
方法:Configure
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.Expand().Filter().Count().OrderBy();
endpoints.MapODataRoute("odata", "odata", AppEdmModel.GetModel());
});
}
usingusing Microsoft.EntityFrameworkCore;
using Microsoft.AspNet.OData.Extensions;
using ODataAuthorizationDemo.Models;
现在,如果我们运行项目并在Postman等工具中访问端点,我们应该看到生成的EDM模型。模型应包含我们指定的不同限制类型的注释:、 和 。这些限制注释中的每一个都应具有我们定义的作用域的属性。GET /odata/$metadataReadRestrictionsInsertRestrictionsUpdateRestrictionsDeleteRestrictionsPermissions
以下是生成的注释的摘录:
<Annotations Target="Default.Container/Products">
<Annotation Term="Org.OData.Capabilities.V1.ReadRestrictions">
<Record>
<PropertyValue Property="Permissions">
<Collection>
<Record>
<PropertyValue Property="SchemeName" String="Scheme" />
<PropertyValue Property="Scopes">
<Collection>
<Record>
<PropertyValue Property="Scope" String="Product.Read" />
</Record>
</Collection>
</PropertyValue>
</Record>
</Collection>
</PropertyValue>
<PropertyValue Property="ReadByKeyRestrictions">
<Record>
<PropertyValue Property="Permissions">
<Collection>
<Record>
<PropertyValue Property="SchemeName" String="Scheme" />
<PropertyValue Property="Scopes">
<Collection>
<Record>
<PropertyValue Property="Scope" String="Product.ReadByKey" />
</Record>
</Collection>
</PropertyValue>
</Record>
</Collection>
</PropertyValue>
</Record>
</PropertyValue>
</Record>
</Annotation>
...
</Annotations>
添加控制器
让我们在文件夹内创建一个来实现我们的 CRUD 操作:ProductsControllerControllers
using System.Threading.Tasks;
using Microsoft.AspNet.OData;
using Microsoft.AspNetCore.Mvc;
using ODataAuthorizationDemo.Models;
namespace ODataAuthorizationDemo.Controllers
{
public class ProductsController: ODataController
{
private AppDbContext _dbContext;
public ProductsController(AppDbContext dbContext)
{
_dbContext = dbContext;
}
public IActionResult Get()
{
return Ok(_dbContext.Products);
}
public IActionResult Get(int key)
{
return Ok(_dbContext.Products.Find(key));
}
public async Task<IActionResult> Post([FromBody] Product product)
{
_dbContext.Products.Add(product);
await _dbContext.SaveChangesAsync();
return Ok(product);
}
public async Task<IActionResult> Update(int key, [FromBody] Delta<Product> delta)
{
var product = await _dbContext.Products.FindAsync(key);
delta.Patch(product);
_dbContext.Products.Update(product);
await _dbContext.SaveChangesAsync();
return Ok(product);
}
public async Task<IActionResult>