14

Django 1.10 で Channels を試し、いくつかのコンシューマーをセットアップしています。

ゲストがこのプライベートソケットに入るのを防ぐために、接続を実行する前に接続を閉じる login_required デコレーターを作成してみました。また、後でユニットテストを統合してテストしましたが、ゲストを入れ続けているため失敗し続けています(どこでもAnonymousUserエラー)。

また、ログインおよびログアウト時にセッションがクリアされず、古いユーザーがログインできる場合があります。

デコレータ:

def login_required_websocket(func):
    """
    If user is not logged in, close connection immediately.
    """
    @functools.wraps(func)
    def inner(message, *args, **kwargs):
        if not message.user.is_authenticated():
            message.reply_channel.send({'close': True})
        return func(message, *args, **kwargs)
    return inner

消費者コードは次のとおりです。

def ws_connect(message, slug):
    message.reply_channel.send({ 'accept': True })
    client = message.reply_channel
    client.send(signal.message("Welcome"))
    try:
        # import pdb; pdb.set_trace()
        Room.objects.get(name=slug)
    except Room.DoesNotExist:
        room = Room.objects.create(name=slug)
        room.users.add(message.user)
        room.turn = message.user.id
        room.save()
        story = Story(room=room)
        story.save()


    # We made sure it exists.
    room = Room.objects.get(name=slug)
    message.channel_session['room'] = room.name

    # Check if user is allowed here.
    if not room.user_allowed(message.user):
        # Close the connection. User is not allowed.
        client.send(Signal.error("User isn't allowed in this room."))
        client.send({'close': True})

奇妙なことに、 client.send(signal.message)) 転送間のすべてのロジックをコメントアウトすると、正常に機能し、単体テストに合格します (ゲストがブロックされ、認証コードが実行されないことを意味します [したがって AnonymousUser エラー])。何か案は?

ここにもテストがあります:

class RoomsTests(ChannelTestCase):

    def test_reject_guest(self):
        """
        This tests whether the login_required_websocket decorator is rejecting guests.
        """
        client = HttpClient()
        user = User.objects.create_user(
            username='test', password='password')

        client.send_and_consume('websocket.connect',
                                path='/rooms/test_room', check_accept=False)
        self.assertEqual(client.receive(), {'close': True})

    def test_accept_logged_in(self):
        """
        This tests whether the connection is accepted when a user is logged in.
        """
        client = HttpClient()
        user = User.objects.create_user(
            username='test', password='password')
        client.login(username='test', password='password')

        client.send_and_consume('websocket.connect', path='/rooms/test_room')

私はこれに間違って近づいていますか?もしそうなら、どうすればこれを適切に行うことができますか(認証が必要ですか?)

編集: 何かを試すためにアクション システムを統合しました。Django チャネルが HTTP からセッションをまったく取得していないようです。

@enforce_ordering
@channel_session_user_from_http
def ws_connect(message, slug):
    message.reply_channel.send({'accept': True})
    message.reply_channel.send(Action.info(message.user.is_authenticated()).to_send())

false を返すだけです。

EDIT2: 動作するようになりました。localhost を 127.0.0.1 に変更してみましたが、動作することがわかりました。localhost を有効なドメインとして検出して、セッションを移植する方法はありますか?

EDIT3: localhost と 127.0.0.1 の Cookie の問題が見つかりました。報奨金を無駄にしないために、メッセージ/チャネルに auth login_required を個人的にどのように実装しますか?

edit4:なぜうまくいかなかったのかはまだわかりませんが、最終的に問題を回避してアプリを変更した方法は次のとおりです。

アクションシステムを作成しました。入力すると、JSON を介して AUTHENTICATE アクションを送信するまで、ソケットは何もしません。ログインしたアクションをguest_actionsとuser_actionsに分けました。認証されると、セッションが設定され、user_actions を使用できるようになります。

4

3 に答える 3

1

あなたの機能は私にとって「そのまま」機能しました。詳細を説明する前に、セッションが閉じられないというバグ(現在は解決済み) があり、それが他の問題を説明している可能性があります。

クラスベースのコンシューマーを使用していたため、「現状のまま」の周りに引用符をほとんど使用していないためself、明示的にテストするためにデコレーターのスタック全体に追加する必要がありました。

class MyRouter(WebsocketDemultiplexer):
    # WebsocketDemultiplexer calls raw_connect for websocket.connect
    @channel_session_user_from_http
    @login_required_websocket
    def raw_connect(self, message, **kwargs):
        ...

実行の順序を確認するためにいくつかのデバッグ メッセージを追加した後:

>>> ws = create_connection("ws://localhost:8085")

# server logging
channel_session_user_from_http.run
login_required_websocket.run
user: AnonymousUser

# client logging
websocket._exceptions.WebSocketBadStatusException: Handshake status 403

>>> ws = create_connection("ws://localhost:8085", cookie='sessionid=43jxki76cdjl97b8krco0ze2lsqp6pcg')

# server logging
channel_session_user_from_http.run
login_required_websocket.run
user: admin

私のスニペットからわかるように、@channel_session_user_from_http最初に電話する必要があります。関数ベースのコンシューマの場合、デコレータに含めることでこれを簡素化できます。

def login_required_websocket(func):
    @channel_session_user_from_http
    @functools.wraps(func)
    def inner(message, *args, **kwargs):
        ...

クラスベースのコンシューマーでは、これは次のように設定することで内部的に (そして正しい順序で) 処理されますhttp_user_and_session

class MyRouter(WebsocketDemultiplexer):
    http_user_and_session = True

selfこれで使用される -respecting デコレータの完全なコードは次のとおりです。

def login_required_websocket(func):
    """
    If user is not logged in, close connection immediately.
    """
    @functools.wraps(func)
    def inner(self, message, *args, **kwargs):
        if not message.user.is_authenticated():
            message.reply_channel.send({'close': True})
        return func(self, message, *args, **kwargs)
    return inner
于 2017-08-29T14:57:29.427 に答える