使用Owin Middleware添加声明

时间:2015-02-05 13:56:59

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

使用Owin Middleware实现是否可以在执行Web API控制器之前添加声明?

创建了一个OwinMiddleware实现并添加了一个标识:

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

 await Next.Invoke(context);

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

想法?

3 个答案:

答案 0 :(得分: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/

答案 1 :(得分: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 :(得分:0)

这是我最终在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;
}