我有一个web api,它通过api密钥,用户访问令牌或客户端访问令牌接受身份验证。我已经针对每个案例编写了DelegatingHandler
,根据给定的身份验证详细信息创建了一个新的ClaimsPrincipal
,并确认在控制器操作中可以访问该主体。
我现在要做的是将公司,用户或发布者注入路由值,这样我就可以在每个案例的控制器上创建重载。在以下条件下插入管道需要扩展什么类/接口:
修改
我不打算在这里回避路由 - 我仍然希望MVC根据路由值为我选择一条路线。我只想在选择之前向路由值添加一个参数,并且参数将具有不同的名称和类型,具体取决于是使用用户访问令牌还是使用api密钥。
我认为身份验证的主题应该导致两种不同的方法,因为api密钥可以访问公司的所有资源,而用户访问令牌只能访问他们有权查看的资源。
答案 0 :(得分:1)
我没有看到你想在这里使用控制器的原因。你会回避路由,一个非常自以为是的MVC。我会create middleware that runs before MVC(它本身就是中间件)而不是。
如果您希望内联RouteData
,我会考虑使用a global IResourceFilter
or IAsyncResourceFilter
。然后,您可以根据您在问题中指定的条件更新the RouteData
property上的ResourceExecutingContext
。
确定如何填充RouteData
属性所需的任何其他依赖项都可以注入资源过滤器的构造函数,如the section on dependency injection中所述。
public class SetRouteValueResourceFilter : IAsyncResourceFilter {
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) {
var company = context.HttpContext.Identity.FindFirst("Company")?.Value;
if (company != null) {
context.RouteData.Values.Add("company", company);
}
await next();
}
}
这个答案只是一个想法。即使这是在动作逻辑之前处理的,我也不确定它是否会影响选择哪条路线。根据{{3}},在运行过滤器之前选择路由。
答案 1 :(得分:1)
我设法通过创建自定义ValueProviderFactory
来实现此目的,该自定义从当前主体的声明中读取值并使它们可用于参数绑定:
public class ClaimsPrincipalValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(HttpActionContext actionContext)
{
if (actionContext.RequestContext.Principal != null && actionContext.RequestContext.Principal is ClaimsPrincipal principal)
{
var pairs = principal.Claims.Select(claim => new KeyValuePair<string, string>(claim.Type, claim.Value));
return new NameValuePairsValueProvider(pairs, CultureInfo.CurrentCulture);
}
return null;
}
}
为了使用它,您可以使用ValueProvider
属性注释输入参数:
public class FooController : ApiController
{
[HttpGet]
public void Bar([ValueProvider(typeof(ClaimsPrincipalValueProviderFactory))]ApiKey apiKey)
{
// ...
}
}
这是非常丑陋和难以理解的,我真正想要的是像FromUri
属性,但对于声明。 ValueProviderAttribute
和FromUriAttribute
都从ModelBinderAttribute
继承,因此我创建了一个同样的类:
/// <summary>
/// The action parameter comes from the user's claims, if the user is authorized.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class FromClaimsAttribute : ModelBinderAttribute
{
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
return parameter.BindWithModelBinding(new ClaimsPrincipalValueProviderFactory());
}
}
现在FooController
上的示例方法看起来更具可读性:
[HttpGet]
public void Bar([FromClaims]ApiKey apiKey)
{
// ...
}
<强>更新强>
看起来这仍然存在路由选择和重载问题,特别是当某些参数可以为空且值为null时。我将不得不继续研究这个问题。
更新#2
在找到内置NameValuePairValueProvider