Web API/OWIN,SignalR和授权

Pet*_*ery 28 c# asp.net signalr angularjs owin

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

在这个阶段,我几乎使用visual studio为个人用户帐户生成的预制代码.

StartUp.Auth.cs代码中有一行看起来像这样.

app.UseOAuthBearerTokens(OAuthOptions);
Run Code Online (Sandbox Code Playgroud)

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

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

$http.defaults.headers.common.Authorization = 'bearer ' + access_token;
Run Code Online (Sandbox Code Playgroud)

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

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

所以我的问题是如何使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);
Run Code Online (Sandbox Code Playgroud)

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

最后回答问题: 这是将承载令牌授权与SignalR和OWIN管道集成的正确模式.
我似乎无法找到正确的方法来做到这一点.

cal*_*oyd 19

我使用这样的类:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

而不是仅仅将令牌传递给标头,解析它并在上下文中设置它.

然后它可以在您的应用程序配置中使用,如下所示:

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"))    
});
Run Code Online (Sandbox Code Playgroud)

然后,以下样式的请求将使其令牌未加密,以用于身份验证和授权:

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
Run Code Online (Sandbox Code Playgroud)

  • 很优雅的解决方案 谢谢! (3认同)

San*_*edo 16

这是我如何解决它

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

服务器端代码没有变化

希望它可以帮助某人

  • 它可以工作,但仅适用于SignalR中的"长轮询"连接类型.为了对WebSockets使用OAuth承载认证,您应该使用查询传递承载令牌并实现自定义的"OAuthBearerAuthenticationProvider",它将从查询参数中提取您的令牌.看看这里:http://stackoverflow.com/questions/26531252/how-do-we-preform-owin-oauth-bearer-token-based-authentication-and-authorization (11认同)
  • 这与在Winsock上javascript客户端无法添加自定义标头的事实相冲突吗?(从我在很多地方读到的内容)如果这是真的,这适用于长轮询和webevents,但不适用于Websocket传输. (3认同)

rad*_*tei 14

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

有多种方法可以使它工作,但取决于应用程序的目的.

  1. 我见过的第一个SignalR使用标题,看起来很容易实现:

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

这是一个巨大的缺点,它强制SignalR使用longPolling,你绝对不想要,因为你使用SignalR.

  1. 第二种方法是在连接之前传递access token客户端在登录时收到的内容query string.然后,制作一个Attribute继承的自定义AuthorizeAttribute(遵循这个回购 - 在我看来并不是很好,但它提出了一个观点).

传递令牌的另一种方法query string遵循这个创建OAuth Provider和的 答案

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

同样,这种方法不是最佳方法,因为query strings它非常脆弱:

查询字符串往往存储在Web服务器日志中(或者即使在使用SSL时也以其他方式公开).有人拦截令牌的风险.您需要选择最适合您的方案的方法.

  1. 我目前正在尝试实现的解决方案(并且一旦工作就会返回更新:D)基于此博客文章,该文章通过将令牌通过cookie传递到管道中来使用OAuth Bearer Token authenticationwith .SignalRSignalR

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

希望这可以帮助.祝你好运!

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

你可以看看这个GitHub回购,但我大多数都是按照上面的文章进行的.明确地说,这就是我所做的:

我创建了一个OAuthBearerTokenAuthenticationProvider继承OAuthBearerAuthenticationProvider并覆盖该RequestToken方法的类.

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

然后,在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如需工作样本,请查看上面的回购.

祝你好运!