【问题标题】:Add claims with Owin Middleware使用 Owin 中间件添加声明
【发布时间】:2015-04-05 10:00:39
【问题描述】:

Owin 中间件实现是否可以在执行 Web API 控制器之前添加声明?

创建了一个 OwinMiddleware 实现并添加了一个身份:

 var id = new ClaimsIdentity();
 id.AddClaim(new Claim("Whatever", "is possible"));
 context.Authentication.User.AddIdentity(id);

 await Next.Invoke(context);

然而,即使是这个 Invoke 方法调用,身份也不会更新(只是内部声明数组)。并且控制器在执行时当然不会获得新的虚拟声明。

想法?

【问题讨论】:

  • 在 JabbR 的 Owin 大厅与 @Pinpoint 聊天后,似乎进入身份验证行的唯一方法是通过 IAppBuilder 的 UseOAuthBearerAuthentication 指定自定义提供程序。然后可以将声明添加到身份上下文中 (context.Ticket.Identity.AddClaim(...))。这是根据请求完成的。

标签: c# asp.net-web-api owin claims-based-identity owin-middleware


【解决方案1】:

已经有一个类可以提供丰富的声明 ClaimsAuthenticationManager,您可以对其进行扩展,以便它处理特定于域的声明,例如...

public class MyClaimsAuthenticationManager : ClaimsAuthenticationManager
{
    public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
    {
        if (!incomingPrincipal.Identity.IsAuthenticated)
        {
            return base.Authenticate(resourceName, incomingPrincipal);
        }

        return AddApplicationClaims(incomingPrincipal);
    }

    private ClaimsPrincipal AddApplicationClaims(ClaimsPrincipal principal)
    {
        // TODO: Add custom claims here based on current principal.

        return principal;
    }
}

下一个任务是提供适当的中间件来调用它。对于我的项目,我编写了以下类...

/// <summary>
/// Middleware component to apply claims transformation to current context
/// </summary>
public class ClaimsTransformationMiddleware
{
    private readonly Func<IDictionary<string, object>, Task> next;
    private readonly IServiceProvider serviceProvider;

    public ClaimsTransformationMiddleware(Func<IDictionary<string, object>, Task> next, IServiceProvider serviceProvider)
    {
        this.next = next;
        this.serviceProvider = serviceProvider;
    }

    public async Task Invoke(IDictionary<string, object> env)
    {
        // Use Katana's OWIN abstractions
        var context = new OwinContext(env);

        if (context.Authentication != null && context.Authentication.User != null)
        {
            var manager = serviceProvider.GetService<ClaimsAuthenticationManager>();
            context.Authentication.User = manager.Authenticate(context.Request.Uri.AbsoluteUri, context.Authentication.User);
        }

        await next(env);
    }
}

然后是接线延长...

public static class AppBuilderExtensions
{
    /// <summary>
    /// Add claims transformation using <see cref="ClaimsTransformationMiddleware" /> any depdendency resolution is done via IoC
    /// </summary>
    /// <param name="app"></param>
    /// <param name="serviceProvider"></param>
    /// <returns></returns>
    public static IAppBuilder UseClaimsTransformation(this IAppBuilder app, IServiceProvider serviceProvider)
    {
        app.Use<ClaimsTransformationMiddleware>(serviceProvider);

        return app;
    }
}

我知道这是服务定位器反模式,但使用 IServiceProvider 是容器中立的,并且似乎是将依赖项放入 Owin 中间件的公认方式。

最后,您需要在 Startup 中将其连接起来,下面的示例假定 Unity 并注册/公开 IServiceLocator 属性...

// Owin config
app.UseClaimsTransformation(UnityConfig.ServiceLocator);

【讨论】:

    【解决方案2】:

    您可能会发现从 Authorizate Attribute 继承并扩展它以满足您的要求很有用:

    public class DemoAuthorizeAttribute : AuthorizeAttribute
        {     
    
            public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext){
                if (Authorize(actionContext)){
                    return;
                }
                HandleUnauthorizedRequest(actionContext);
            }
    
            protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext){
                var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized;
    
    //Adding your code here
     var id = new ClaimsIdentity();
     id.AddClaim(new Claim("Whatever", "is possible"));
     context.Authentication.User.AddIdentity(id);
    
                challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
                throw new HttpResponseException(challengeMessage);
            }
    
            private bool Authorize(System.Web.Http.Controllers.HttpActionContext actionContext){
                try{
                    var someCode = (from h in actionContext.Request.Headers where h.Key == "demo" select h.Value.First()).FirstOrDefault();
    
                     // or check for the claims identity property.
                    return someCode == "myCode";
                }
                catch (Exception){
                    return false;
                }
            }
        }
    

    在你的控制器中:

    [DemoAuthorize]
    public class ValuesController : ApiController{
    

    这里是 WebApi 授权的其他自定义实现的链接:

    http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-membership-provider/

    【讨论】:

    • 我发现的唯一问题是您是否支持多种授权方法,例如OAuth 和 HMAC 您必须在多个地方实现声明扩充,因此我的答案如下。
    【解决方案3】:

    这就是我最终在 owin 中间件中添加新声明的方式,基于 OP 关于挂钩 UseOAuthBearerAuthentication 的评论。它使用 IdentityServer3.AccessTokenValidation,它在内部调用 UseOAuthBearerAuthentication 并将 OAuthBearerAuthenticationProvider 传递给它。

    using System.Security.Claims;
    using System.Threading.Tasks;
    using IdentityServer3.AccessTokenValidation;
    using Owin;
    using Microsoft.Owin.Security.OAuth;
    
    //...
    
    public void Configuration(IAppBuilder app)
    {
        app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
        {
            Authority = "http://127.0.0.1/identityserver", 
            TokenProvider = new OAuthBearerAuthenticationProvider
            {
                OnValidateIdentity = AddClaim
            }
        });
    }
    
    private Task AddClaim(OAuthValidateIdentityContext context)
    {
        context.Ticket.Identity.AddClaim(new Claim("test", "123"));
        return Task.CompletedTask;
    }
    

    【讨论】:

      猜你喜欢
      • 2017-08-05
      • 1970-01-01
      • 1970-01-01
      • 2014-09-28
      • 2019-02-17
      • 2014-09-29
      • 2015-03-18
      • 1970-01-01
      • 2014-10-27
      相关资源
      最近更新 更多