【问题标题】:Get IPrincipal from OAuth Bearer Token in OWIN从 OWIN 中的 OAuth Bearer Token 获取 IPrincipal
【发布时间】:2014-01-02 08:45:55
【问题描述】:

我已使用 OWIN 成功地将 OAuth 添加到我的 WebAPI 2 项目中。我收到令牌并可以在 HTTP 标头中使用它们来访问资源。

现在我想在其他通道上使用这些令牌进行身份验证,这些通道不是 OWIN 模板所针对的标准 HTTP 请求。例如,我使用 WebSockets,客户端必须发送 OAuth Bearer Token 进行身份验证。

在服务器端,我通过 WebSocket 接收令牌。 但是我现在如何将此令牌放入 OWIN 管道中以从中提取 IPrincipal 和 ClientIdentifier? 在 WebApi 2 模板中,所有这些对我来说都是抽象的,所以我无需做任何事情让它工作。

所以,基本上,我将令牌作为字符串,并希望使用 OWIN 访问在该令牌中编码的用户信息。

提前感谢您的帮助。

【问题讨论】:

    标签: c# .net oauth asp.net-web-api owin


    【解决方案1】:

    我在这篇博文中找到了部分解决方案:http://leastprivilege.com/2013/10/31/retrieving-bearer-tokens-from-alternative-locations-in-katanaowin/

    所以我创建了自己的Provider如下:

    public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
    {
        public override Task RequestToken(OAuthRequestTokenContext context)
        {
            var value = context.Request.Query.Get("access_token");
    
            if (!string.IsNullOrEmpty(value))
            {
                context.Token = value;
            }
    
            return Task.FromResult<object>(null);
        }
    }
    

    然后我需要像这样在 Startup.Auth.cs 中将它添加到我的应用程序中:

    OAuthBearerOptions = new OAuthBearerAuthenticationOptions()
    {
       Provider = new QueryStringOAuthBearerProvider(),
       AccessTokenProvider = new AuthenticationTokenProvider()
       {
           OnCreate = create,
           OnReceive = receive
       },
    };
    
    app.UseOAuthBearerAuthentication(OAuthBearerOptions);
    

    使用自定义 AuthenticationTokenProvider,我可以在管道的早期从令牌中检索所有其他值:

    public static Action<AuthenticationTokenCreateContext> create = new Action<AuthenticationTokenCreateContext>(c =>
    {
        c.SetToken(c.SerializeTicket());
    });
    
    public static Action<AuthenticationTokenReceiveContext> receive = new Action<AuthenticationTokenReceiveContext>(c =>
    {
        c.DeserializeTicket(c.Token);
        c.OwinContext.Environment["Properties"] = c.Ticket.Properties;
    });
    

    现在,例如在我的 WebSocket Hander 中,我可以像这样检索 ClientId 和其他人:

    IOwinContext owinContext = context.GetOwinContext();
    if (owinContext.Environment.ContainsKey("Properties"))
    {
        AuthenticationProperties properties = owinContext.Environment["Properties"] as AuthenticationProperties;
        string clientId = properties.Dictionary["clientId"];
    ...
     }
    

    【讨论】:

    • 不知道你是怎么想出来的。我找不到任何关于它的文档,但非常感谢!
    【解决方案2】:

    默认情况下,当托管在 IIS 上时,OWIN 使用 ASP.NET 机器密钥数据保护来保护 OAuth 访问令牌。您可以使用 System.Web.dll 中的 MachineKey 类来取消对令牌的保护。

    public class MachineKeyProtector : IDataProtector
    {
        private readonly string[] _purpose =
        {
            typeof(OAuthAuthorizationServerMiddleware).Namespace,
            "Access_Token",
            "v1"
        };
    
        public byte[] Protect(byte[] userData)
        {
           throw new NotImplementedException();
        }
    
        public byte[] Unprotect(byte[] protectedData)
        {
            return System.Web.Security.MachineKey.Unprotect(protectedData, _purpose);
        }
    }
    

    然后,构造一个 TicketDataFormat 来获取 AuthenticationTicket 对象,您可以在其中获取 ClaimsIdentity 和 AuthenticationProperties。

    var access_token="your token here";
    var secureDataFormat = new TicketDataFormat(new MachineKeyProtector());
    AuthenticationTicket ticket = secureDataFormat.Unprotect(access_token);
    

    要取消保护其他 OAuth 令牌,您只需更改 _purpose 内容。有关详细信息,请参阅此处的 OAuthAuthorizationServerMiddleware 类: http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.OAuth/OAuthAuthorizationServerMiddleware.cs

    if (Options.AuthorizationCodeFormat == null)
    {
        IDataProtector dataProtecter = app.CreateDataProtector(
            typeof(OAuthAuthorizationServerMiddleware).FullName,
            "Authentication_Code", "v1");
    
        Options.AuthorizationCodeFormat = new TicketDataFormat(dataProtecter);
    }
    if (Options.AccessTokenFormat == null)
    {
        IDataProtector dataProtecter = app.CreateDataProtector(
            typeof(OAuthAuthorizationServerMiddleware).Namespace,
            "Access_Token", "v1");
        Options.AccessTokenFormat = new TicketDataFormat(dataProtecter);
    }
    if (Options.RefreshTokenFormat == null)
    {
        IDataProtector dataProtecter = app.CreateDataProtector(
            typeof(OAuthAuthorizationServerMiddleware).Namespace,
            "Refresh_Token", "v1");
        Options.RefreshTokenFormat = new TicketDataFormat(dataProtecter);
    }
    

    【讨论】:

    • 所以这意味着您必须在 webapi 和其他应用程序(比如一些独立的 signalR 控制台应用程序)上使用相同的保护程序?
    • 那么我们无法访问 System.Web 的自托管 owin 应用程序呢?
    • @MahmoudMoravej 然后你必须使用 DPAPI DataProtector 而不是 MachineKeyDataProtector。
    • 是的,就像我在下面的答案中解释的那样
    【解决方案3】:

    除了johnny-qian的回答,用这种方法创建DataProtector更好。 johnny-qian 答案,取决于 IIS,在自托管方案中失败。

    using Microsoft.Owin.Security.DataProtection;
    var dataProtector = app.CreateDataProtector(new string[]   {
         typeof(OAuthAuthorizationServerMiddleware).Namespace,
         "Access_Token",
         "v1"
    });
    

    【讨论】:

      【解决方案4】:

      你的令牌是什么样的,是加密字符串还是格式化字符串,格式是什么?

      我的代码:

      public static Action<AuthenticationTokenReceiveContext> receive = new Action<AuthenticationTokenReceiveContext>(c =>
      {
              if (!string.IsNullOrEmpty(c.Token))
              {
      
                  c.DeserializeTicket(c.Token);
                  //c.OwinContext.Environment["Properties"] = c.Ticket.Properties;
              }
      });
      

      c.Ticket 始终为空。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-12
        • 2015-12-25
        • 2023-03-15
        • 2014-09-24
        • 2015-12-31
        • 2015-06-12
        • 2014-12-03
        • 1970-01-01
        相关资源
        最近更新 更多