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.6
、channels 2.1.1
、angular 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]);
我有一个 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.6
、channels 2.1.1
、angular 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]);