【问题标题】:Web API / OWIN, SignalR & AuthorizationWeb API / OWIN、SignalR 和授权
【发布时间】:2014-05-24 05:29:34
【问题描述】:

我正在开发 AngularJS、Web API、SignalR 应用程序的原型,作为 VS 2013 中新项目的潜在起点。

在这个阶段,我主要使用 Visual Studio 为个人用户帐户生成的预制代码。

StartUp.Auth.cs 代码中有一行如下所示。

app.UseOAuthBearerTokens(OAuthOptions);

有了这个,我可以将 [Authorize] 属性添加到控制器,它工作正常。

顺便说一句,使用 angular,我可以在 JavaScript 中添加一个包含标记的标准标头,如下所示。

$http.defaults.headers.common.Authorization = 'bearer ' + access_token;

然后我将 SignalR 添加到项目中。
它支持自己的 [Authorize] 属性版本,但在使用 SignalR 时无法传递自定义标头。
这是浏览器端的限制。
文档说您可以将令牌作为查询字符串的一部分传递。
我在 JavaScript 端添加了该代码。我的 SignalR 代码现在包含此内容。
我将令牌作为“bearer_token”传递。

this.connection = $.hubConnection("/TestHub", { useDefaultPath: false, qs: "bearer_token=" + token });

所以我的问题是如何让 OWIN 识别令牌,现在它不再在标头中。
经过多次搜索后,我最终添加了将令牌从查询字符串移至标头的代码。
对于我的原型,我只是在 StartUp.Auth.cs 的原始行之上添加了一点代码。

所以,现在看起来像这样:

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
    Provider = new OAuthBearerAuthenticationProvider()
    {
        OnRequestToken = context =>
        {
            if (context.Request.Path.Value.StartsWith("/TestHub"))
            {
                string bearerToken = context.Request.Query.Get("bearer_token");
                if (bearerToken != null)
                {
                    string[] authorization = new string[] { "bearer " + bearerToken };
                    context.Request.Headers.Add("Authorization", authorization);
                }
            }

            return Task.FromResult(context);
        }
    }
});


// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);

上面的代码很粗糙,但这是一个原型,所以我真的只是想看看它是否有效。

终于要回答这个问题了: 这是将不记名令牌授权与 SignalR 和 OWIN 管道集成的正确模式吗?
我似乎找不到很多关于正确方法的好信息。

【问题讨论】:

  • 现在可以工作了吗?这可能是我见过的最好的尝试。
  • 它工作正常。只是很少有指导知道如何去做。谢谢。
  • 我做了类似的事情,但我已经将它封装在一个 OWIN 中间件模块中。
  • 奇怪地设置标题对我没有任何作用...我必须将 cookie 值放入上下文... if (!string.IsNullOrEmpty(bearerToken)) { context.Token = BearerToken; }

标签: c# asp.net angularjs signalr owin


【解决方案1】:

我会为将来遇到此问题的人发布此消息:

有多种方法可以使其正常工作,但取决于应用程序的目的。

  1. 我看到的第一个使 SignalR 与标头一起工作,这似乎很容易实现:

    $.signalR.ajaxDefaults.headers = { Authorization: "Bearer " + token };

这样做的巨大缺点是它迫使 SignalR 使用 longPolling,这是你绝对不想要的,因为你正在使用 SignalR。

  1. 第二种方法是在连接之前传递客户端在作为query string 登录时收到的access token。然后,创建一个继承 AuthorizeAttribute (follow this repo - not great in my opinion, but it makes a point) 的自定义 Attribute

另一种将令牌作为query string 传递的方法是follow this SO answer,它会创建一个OAuth Provider

在管道早期从令牌中检索所有其他值

同样,这种方法不是最佳方法,因为query strings 很容易受到攻击:

查询字符串往往存储在网络服务器日志中(或暴露在 即使使用 SSL 也有其他方式)。有人有风险 拦截令牌。您需要选择适合您的方法 最好的场景。

  1. 我目前正在尝试实施的解决方案(一旦它起作用就会返回更新:D)是based on this blog post,它使用OAuth Bearer Token authenticationSignalR,通过将令牌通过cookie 传递到SignalR 管道.

我相信这是妥协最少的解决方案,但一旦我的实施完成,我会回来提供更多信息。

希望这会有所帮助。祝你好运!

更新 通过将令牌存储在 cookie 中,然后在提供程序中检索它,我设法让 WebApi 令牌身份验证与 SignalR 一起使用。

您可以选择look at this GitHub repo,但我主要是从上面的文章中进行操作。明确地说,这就是我所做的:

我创建了一个继承自OAuthBearerAuthenticationProviderOAuthBearerTokenAuthenticationProvider 类并覆盖了RequestToken 方法。

现在它会查找存储 BearerToken 的 cookie 并检索其值。然后,它将context.Token 设置为cookie 的值。

然后,在 JavaScript 部分,您必须获取令牌(通过使用用户名和密码进行身份验证)并将令牌存储在 cookie 中。

public class OAuthBearerTokenAuthenticationProvider : OAuthBearerAuthenticationProvider
{
    public override Task RequestToken(OAuthRequestTokenContext context)
    {
        var tokenCookie = context.OwinContext.Request.Cookies["BearerToken"];

        if (!String.IsNullOrEmpty(tokenCookie))
            context.Token = tokenCookie;

        return Task.FromResult<object>(null);
    }
}

有关工作示例,请查看上面的 repo。

祝你好运!

【讨论】:

  • 你有什么运气/成功吗?
  • 最后我不得不放弃不记名令牌身份验证,只使用 cookie 身份验证。这只是整体上更简单的解决方案。
  • 感谢您的回复!
  • @overslacked 我更新了我的答案并添加了有效的 GitHub 存储库
【解决方案2】:

我使用这样的类:

public class OAuthTokenProvider : OAuthBearerAuthenticationProvider
{
    private List<Func<IOwinRequest, string>> _locations;
    private readonly Regex _bearerRegex = new Regex("((B|b)earer\\s)");
    private const string AuthHeader = "Authorization";

    /// <summary>
    /// By Default the Token will be searched for on the "Authorization" header.
    /// <para> pass additional getters that might return a token string</para>
    /// </summary>
    /// <param name="locations"></param>
    public OAuthTokenProvider(params Func<IOwinRequest, string>[] locations)
    {
        _locations = locations.ToList();
        //Header is used by default
        _locations.Add(x => x.Headers.Get(AuthHeader));
    }

    public override Task RequestToken(OAuthRequestTokenContext context)
    {
        var getter = _locations.FirstOrDefault(x => !String.IsNullOrWhiteSpace(x(context.Request)));
        if (getter != null)
        {
            var tokenStr = getter(context.Request);
            context.Token = _bearerRegex.Replace(tokenStr, "").Trim();
        }
        return Task.FromResult<object>(null);
    }
}

这不仅仅是将令牌传递给标头,而是对其进行解析并将其设置在上下文中。

然后它可以像这样在您的应用配置中使用:

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
    Provider = new OAuthTokenProvider(
         req => req.Query.Get("bearer_token"),
         req => req.Query.Get("access_token"),
         req => req.Query.Get("token"),
         req => req.Headers.Get("X-Token"))    
});

那么以下类型的请求将使其令牌未加密,以用于身份验证和授权:

GET https://www.myapp.com/authorized/endpoint?bearer_token=123ABC HTTP/1.1
GET https://www.myapp.com/authorized/endpoint?access_token=123ABC HTTP/1.1
GET https://www.myapp.com/authorized/endpoint?token=123ABC HTTP/1.1

GET https://www.myapp.com/authorized/endpoint HTTP/1.1
X-Token: 123ABC

GET https://www.myapp.com/authorized/endpoint HTTP/1.1
Authorization: 123ABC

【讨论】:

  • +1 能否请您显示更多代码,特别是配置/启动代码和客户端?
  • 这有帮助吗?对于带有信号器的 js 客户端,我认为它类似于:jQuery.hubConnection(url,{qs:"access_token="+token})
  • @calebboyd +1 谢谢,正是我所追求的。您究竟是如何设置 X-TokenAuthorization 标头的?
  • 在令牌端点的响应处理程序中,设置一个默认标头...我猜具体取决于您的 xhr 抽象...
  • 非常优雅的解决方案!谢谢!
【解决方案3】:

我就是这样解决的

var authData = localStorageService.get('authorizationData');
var token = authData.token;
 $.signalR.ajaxDefaults.headers = { Authorization: "Bearer " + token };

服务器端代码没有变化

希望对某人有所帮助

【讨论】:

  • 这是否与 Winsock 上的 javascript 客户端无法添加自定义标头这一事实相冲突? (据我在 SO 的许多地方读到的)如果这是真的,这适用于长轮询和 webevents,但不适用于 Websocket 传输。
  • @pomarc 你确认过吗?我读过类似的东西。
  • 它有效,但仅适用于 SignalR 中的“长轮询”连接类型。要通过 WebSockets 使用 OAuth 不记名身份验证,您应该通过查询传递不记名令牌并实现自定义 OAuthBearerAuthenticationProvider,这将从查询参数中提取您的令牌。看这里:stackoverflow.com/questions/26531252/…
  • 值得注意的是,在这样的查询参数中使用令牌会带来轻微的安全风险,因为它可能会被记录在服务器上。
  • @KingOfHypocrites - pomarc 说的是真的。我对此进行了测试。使用 Bounz 添加的链接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-24
  • 1970-01-01
  • 2016-04-04
  • 1970-01-01
  • 2015-03-19
  • 2015-10-05
相关资源
最近更新 更多