在 Web 中使用依赖注入进行服务授权 API 2
Service Authorization with Dependency Injection in Web API 2
我有一个 n 层网络 api 2 项目,其结构如下:
- 网络层
- Service/Business层
- Data/Repository层
Web 控制器使用 Autofac 注入了相应的服务,并且每个服务都注入了相应的存储库。这很好用。
现在,出于业务逻辑目的,我需要注入一个对象,该对象包含有关用户的一些信息。类似这样:
public class OrderService : IOrderService
{
private readonly IOrderRepo _Repo;
private readonly EnterpriseUser _EnterpriseUser;
public OrderService(IOrderRepo repo, EnterpriseUser enterpriseUser)
{
this._Repo = repo;
this._EnterpriseUser = enterpriseUser;
}
....
}
需要用户信息,例如,如果他们具有一定的特权级别,那么他们的订单可能会被放入队列中以供管理员审查,或者他们可能会获得折扣。
我的问题出在身份验证过程中。 EnterpriseUser
需要来自当前 IIdentity
的声明。
builder.Register(ctx =>
{
// Identity extension to retrieve claim from User
var userLinkCode = ctx.Resolve<HttpContextBase>().User.Identity.GetUserLinkCode();
// Retrieves a header from the request
var partnerLinkCode = ctx.Resolve<ConnectionInfo>().PartnerLinkCode;
return EnterpriseUser.Create(userLinkCode, partnerLinkCode);
})
.As<EnterpriseUser>()
.InstancePerRequest();
所以当用户的token过期时,Autofac会抛出如下异常
A delegate registered to create instances of 'LVV.Services.EnterpriseUser' returned null
因为 Claims 是空的,并且 AuthorizeAttribute
永远无法拒绝使用 401 Unauthorized
的调用,因为控制器的构造函数在 AuthorizeAttribute
之前运行。
是否有已知的解决方法?
如果委托 returns 为空,可能有一种方法可以拒绝使用 401
的呼叫?
或者更好的方式将有关用户的信息注入服务层,同时仍然允许 OAuth 身份验证过程发挥作用。
我建议在这里实现 NullObject pattern 让 AuthorizeAttribute
完成它的工作而不是将 Autofac 和构造函数与原始空值混淆。我没有看到你的整个代码,所以下面的例子只是这个想法的一个例子。假设我们以这种方式扩展 EnterpriseUser
:
public class EnterpriseUser
{
/// <summary>
/// Always true for real user
/// </summary>
public virtual bool IsAuthentificated => true;
public static EnterpriseUser Create(string userLinkCode, string partnerLinkCode)
{
if (string.IsNullOrEmpty(userLinkCode) ||
string.IsNullOrEmpty(partnerLinkCode))
{
return new DummyEnterpriseUser();
}
// create true user here with usin link codes
return new EnterpriseUser(userLinkCode, partnerLinkCode);
}
// Some usefull code here
}
/// <summary>
/// NullObject implementation for <see cref="EnterpriseUser"/>
/// </summary>
public class DummyEnterpriseUser : EnterpriseUser
{
/// <summary>
/// Always false for dummy user
/// </summary>
public override bool IsAuthentificated => false;
public DummyEnterpriseUser()
{
}
}
添加 IsAuthentificated
标志以防万一,您可能不需要它。我还假设您的扩展方法 GetUserLinkCode
returns null
以防出现空声明。当然,最终实现将取决于您的代码。
我有一个 n 层网络 api 2 项目,其结构如下:
- 网络层
- Service/Business层
- Data/Repository层
Web 控制器使用 Autofac 注入了相应的服务,并且每个服务都注入了相应的存储库。这很好用。
现在,出于业务逻辑目的,我需要注入一个对象,该对象包含有关用户的一些信息。类似这样:
public class OrderService : IOrderService
{
private readonly IOrderRepo _Repo;
private readonly EnterpriseUser _EnterpriseUser;
public OrderService(IOrderRepo repo, EnterpriseUser enterpriseUser)
{
this._Repo = repo;
this._EnterpriseUser = enterpriseUser;
}
....
}
需要用户信息,例如,如果他们具有一定的特权级别,那么他们的订单可能会被放入队列中以供管理员审查,或者他们可能会获得折扣。
我的问题出在身份验证过程中。 EnterpriseUser
需要来自当前 IIdentity
的声明。
builder.Register(ctx =>
{
// Identity extension to retrieve claim from User
var userLinkCode = ctx.Resolve<HttpContextBase>().User.Identity.GetUserLinkCode();
// Retrieves a header from the request
var partnerLinkCode = ctx.Resolve<ConnectionInfo>().PartnerLinkCode;
return EnterpriseUser.Create(userLinkCode, partnerLinkCode);
})
.As<EnterpriseUser>()
.InstancePerRequest();
所以当用户的token过期时,Autofac会抛出如下异常
A delegate registered to create instances of 'LVV.Services.EnterpriseUser' returned null
因为 Claims 是空的,并且 AuthorizeAttribute
永远无法拒绝使用 401 Unauthorized
的调用,因为控制器的构造函数在 AuthorizeAttribute
之前运行。
是否有已知的解决方法?
如果委托 returns 为空,可能有一种方法可以拒绝使用 401
的呼叫?
或者更好的方式将有关用户的信息注入服务层,同时仍然允许 OAuth 身份验证过程发挥作用。
我建议在这里实现 NullObject pattern 让 AuthorizeAttribute
完成它的工作而不是将 Autofac 和构造函数与原始空值混淆。我没有看到你的整个代码,所以下面的例子只是这个想法的一个例子。假设我们以这种方式扩展 EnterpriseUser
:
public class EnterpriseUser
{
/// <summary>
/// Always true for real user
/// </summary>
public virtual bool IsAuthentificated => true;
public static EnterpriseUser Create(string userLinkCode, string partnerLinkCode)
{
if (string.IsNullOrEmpty(userLinkCode) ||
string.IsNullOrEmpty(partnerLinkCode))
{
return new DummyEnterpriseUser();
}
// create true user here with usin link codes
return new EnterpriseUser(userLinkCode, partnerLinkCode);
}
// Some usefull code here
}
/// <summary>
/// NullObject implementation for <see cref="EnterpriseUser"/>
/// </summary>
public class DummyEnterpriseUser : EnterpriseUser
{
/// <summary>
/// Always false for dummy user
/// </summary>
public override bool IsAuthentificated => false;
public DummyEnterpriseUser()
{
}
}
添加 IsAuthentificated
标志以防万一,您可能不需要它。我还假设您的扩展方法 GetUserLinkCode
returns null
以防出现空声明。当然,最终实现将取决于您的代码。