websockets和身份验证服务器身份验证

gon*_*ong 7 websocket asp.net-identity asp.net-core

我使用.net core 1.1和identityserver 4来获取令牌并验证用户.web api可以很好地从标题中读取持有者令牌并获得用户主要声明.

现在我想使用websocket(而不是SignalR)来发送通知.我可以打开一个ws:// channel(或wss),但令牌不随头文件一起发送,因此在.net核心应用程序中我没有用户信息(用户声明和身份).

如何通过websocket验证用户身份?我做了搜索,但找不到任何有用的信息.

谢谢

Max*_*lov 9

WebSocket中间件中的身份验证有两个主要问题:

应手动调用授权

首先,授权不适用于Web套接字请求(因为它不是可以用Authorize属性标记的控制器).这就是为什么在WebSocket中间件中你需要自己调用授权.通过调用对象的AuthenticateAsync扩展方法很容易实现HttpContext.

所以,你的中间件看起来像这样:

public class WebSocketMiddleware
{
    private readonly RequestDelegate next;
    public WebSocketMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        if (!context.WebSockets.IsWebSocketRequest)
        {
            await this.next.Invoke(context);
            return;
        }

        AuthenticateResult authenticateResult = 
            await context.AuthenticateAsync(OAuthValidationDefaults.AuthenticationScheme);

         ....
        });
    }
Run Code Online (Sandbox Code Playgroud)

因此,使用身份验证结果,您可以检查用户是否经过身份验证,然后访问经过身份验证的用户信息.

将持有者令牌传递给Web套接字请求

对于Web Socket连接,默认的Authorization标头不起作用,因为WebSockets JS API不允许设置自定义参数.要解决此限制,访问令牌会在查询字符串中经常传递.

要使身份验证中间件能够使用它,您需要更新身份验证验证选项.这基本上可以在你的启动脚本中完成,如下所示:

services
    .AddAuthentication()
    .AddOAuthValidation(options =>
    {
        options.Events = new OAuthValidationEvents
        {
            // Note: for Web Socket connections, the default Authorization header does not work,
            // because the WebSockets JS API doesn't allow setting custom parameters.
            // To work around this limitation, the access token is retrieved from the query string.
            OnRetrieveToken = context =>
            {
                context.Token = context.Request.Query["access_token"];
                return Task.FromResult(0);
            }
        };
    });
Run Code Online (Sandbox Code Playgroud)

以下代码可用作在连接初始化期间将访问令牌添加到Web套接字URL的示例:

const protocol = location.protocol === "https:" ? "wss:" : "ws:";
const wsUri = protocol + "//" + window.location.host + "/ws" + "?access_token=" + token;
this.socket = new WebSocket(wsUri);
Run Code Online (Sandbox Code Playgroud)