WebSocket 握手认证

WebSocket handshake authentication

我有一个 angular 6 应用程序和 python(django 2.0.6) 后端。 Angular 6 个应用程序是使用 AWS S3 和 CloudFront 部署的。

Angular 6 个应用程序使用 websocket 功能。 一切都在本地运行良好,但部署时 Angular 6 应用程序无法通过 websocket 握手请求传递 cookie。 (仅对 websoket 连接通过 cookie 授权。这是有原因的,无关紧要。)

位于子域的 Websocket 服务器:`api.site.com``

我在angular 6 app中尝试了多种设置cookies的方法,例如:

let domain = 'api.site.com'

document.cookie = `Token=${token}; domain=${domain}; path=/`;
document.cookie = `Token=${token}; path=/`;
document.cookie = `Token=${token};
document.cookie = `Token=${token}, domain=${domain}, path=/`;

使用 ngx-cookie-service:

this.cookieService.set('Token', token, undefined, '/', 'api.site.com');
this.cookieService.set('Token', token, undefined, '/');
this.cookieService.set('Token', token);

token 是一个 JWT 令牌。

对于上述所有情况,Token 在部署应用程序时,cookie 不会通过 websocket 握手传递。使用 wss 协议。 angular 6 个应用程序在域 site.com 下运行(=> api 是一个子域)

Cloudfront 行为设置为传递所有 cookie。

请指教可能的原因。

解决方案:

在我的例子中,我无法设置 cookie,但基于这个主题提出了一个解决方案:

有很多关于如何使用 jwt 令牌验证 WebSocket 客户端 API 的零散信息。这是对 django 2.0.6channels 2.1.1angular 6 堆栈有效的完整解决方案:

django 中间件:

class TokenAuthMiddleware:

    def __init__(self, inner):
        self.inner = inner

    def __call__(self, scope):
        auth_header = None
        if 'subprotocols' in scope:
            try:
                auth_header = scope['subprotocols'][1]
            except:
                pass

        if auth_header:
            try:
                user_jwt = jwt.decode(
                    auth_header,
                    settings.SECRET_KEY,
                )
                scope['user'] = MyUser.objects.get(
                    id=user_jwt['user_id']
                )
                close_old_connections()
            except (InvalidSignatureError, KeyError, ExpiredSignatureError, DecodeError):
                scope['auth_error'] = 'KeyError'
                pass
            except Exception as e:  # NoQA
                scope['auth_error'] = 'Unknown'

        return self.inner(scope)


TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))

ws 消费者:

class WsConsumer(JsonWebsocketConsumer):
    def connect(self):
        self.accept('auth_token')
        if self._is_authenticated():
            ***do things***
        else:
            logger.error("ws client auth error")
            self.close(code=4003)

    def _is_authenticated(self):
        if hasattr(self.scope['headers'], 'auth_error'):
            return False
        if type(self.scope['user']) is AnonymousUser or not self.scope['user']:
            return False
        return True

WebSocket 客户端API (js cli):

  this.socket = new WebSocket('ws://host.com/ws/`, ['auth_token', token]);