如何通过 Django Channels 将 cookie 添加到标头?

Posted

技术标签:

【中文标题】如何通过 Django Channels 将 cookie 添加到标头?【英文标题】:How to add cookie to header through Django Channels? 【发布时间】:2019-04-26 21:35:53 【问题描述】:

在我的前端,我有以下配置。身份验证是手动的,因为 react native 在持久会话时失败:

...
const authLink = setContext(request =>
  storage.getCookie().then(cookie => (
    // Before the request is sent: set cookie header
    headers: 
      cookie,
    ,
    credentials: 'omit', // set cookies manually
  ))
)

const setTokenLink = new ApolloLink((operation, forward) => 
  // Send the request to the server
  return forward(operation).map(response => 
    // After response is returned: store cookie in local storage
    const context = operation.getContext()
    const 
      response:  headers ,
     = context

    if (headers) 
      storage.setCookie(headers.get('set-cookie'))
    

    return response
  )
)
storage.getCookie().then(cookie => console.log(cookie))
// console.log('cookie', storage.getCookie().then(cookie => cookie))

const httpLink = createUploadLink(
  uri: `$domainApi/graphql/`,
)

const wsClient = new SubscriptionClient(
  domainWs,
  
    lazy: true,
    reconnect: true,
    connectionParams: 
      authToken: storage.getCookie(),
    ,
  )
)
...

但这似乎会影响 websockets,因为在后端,标头不包含 cookie。

class GraphqlSubcriptionConsumer(SyncConsumer):
    def __init__(self, scope):
        super().__init__(scope)
        self.subscriptions = 
        self.groups = 

    def websocket_connect(self, message):
        self.send("type": "websocket.accept", "subprotocol": "graphql-ws")

    def websocket_disconnect(self, message):
        for group in self.groups.keys():
            group_discard = async_to_sync(self.channel_layer.group_discard)
            group_discard(f"django.group", self.channel_name)

        self.send("type": "websocket.close", "code": 1000)
        raise StopConsumer()

    def websocket_receive(self, message):
        request = json.loads(message["text"])
        id = request.get("id")

        if request["type"] == "connection_init":
            return

        elif request["type"] == "start":
            print("scope user:", self.scope["user"]) //returns AnonymousUser
            print("scope headers:", self.scope["headers"]) //returns headers without the cookie
...

有了这个配置,我得到了

'type': 'websocket.receive', 'text': '"type":"connection_init","payload":"authToken":"_40":0,"_65":1,"_55":"sessionid=bci028bzn1cgxyuynvb7fjevc5ynqdil","_72":null'

在第一条类型为connection_init 的消息中。所以我想知道是否有办法手动将 django 中的 cookie(可能在 if request["type"] == "connection_init": 内)插入作用域,以便start 类型的后续消息包含 cookie。

任何小提示/提示都会有所帮助。

【问题讨论】:

【参考方案1】:

在服务器的握手响应中设置 cookie(使用 channels==2.3.1 测试):

def connect(self):  # connect method of your WebsocketConsumer
    # called on connection
    headers = 
    headers['Set-Cookie'] = 'myCookie=myValue'
    self.accept(subprotocol=(None, headers))

一旦建立连接,就无法通过Set-Cookie HTTP 响应标头从服务器向用户代理发送 cookie。那是因为它是一个开放的 TCP 套接字,协议不再是 HTTP。

Django Channels 在后台使用autobahn。 https://github.com/crossbario/autobahn-python/blob/9e68328df3c23ca3eee67017404235024f265693/autobahn/websocket/types.py#L285

【讨论】:

以上是关于如何通过 Django Channels 将 cookie 添加到标头?的主要内容,如果未能解决你的问题,请参考以下文章

Django使用Channels实现WebSocket--下篇

使用Django Channels 2上传文件

使用 Django Channels 2 上传文件

如何从 Django Channels Web 套接字数据包中获取当前用户?

[Django] [channels] [python]如何上传静态文件(图像,文档)?

通过 celery 向 django-channels 发送消息