9

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方法,身份也不会更新(只是内部声明数组)。并且控制器在执行时当然永远不会获得新的虚拟声明。

想法?

4

3 回答 3

4

已经有一个可以提供声明丰富的类 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 中间件的公认方式。

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

// Owin config
app.UseClaimsTransformation(UnityConfig.ServiceLocator);
于 2016-11-16T15:54:36.440 回答
1

您可能会发现从 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/

于 2015-02-05T14:00:11.407 回答
1

这就是我最终在 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;
}
于 2016-11-23T16:54:46.967 回答