如何使用Nginx进行WebSocket身份验证。 (我将JWT与“ auth_request”模块一起使用)

时间:2020-09-27 03:24:54

标签: security nginx jwt

如何使用Nginx验证websocket? 当我在Nginx中设置“ auth_request”模块时,服务器没有收到请求并发送了Chrome输出:

WebSocketSubject.ts:259 WebSocket连接到 'ws:// localhost / ws / videos'失败:HTTP身份验证失败;无效 可用凭证

这是我的设置:

#client
upstream client {
    server localhost:3000;
}

upstream authentication {
    server localhost:4000; 
}

upstream rtserver {
    server localhost:6000;
}

server {
    listen        80;
    listen        [::]:80;
    server_name localhost;

    root /home/guy/Documents/workspace/project/public;

    location / {
        proxy_pass http://client/; 
    }

    location /sockjs-node {
        proxy_pass http://client/sockjs-node;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_cache_bypass $http_upgrade;
    }   
 
    location /ws/videos { 
        auth_request /auth;
        auth_request_set $auth_status $upstream_status;

        proxy_pass http://rtserver/ws/videos;
        proxy_set_header   X-Forwarded-For $remote_addr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'Upgrade';
        proxy_set_header Host $host; 

        proxy_set_header Set-Cookie $cookie_video;

        proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol;
        proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
        proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
        proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
    } 

    location = /users/login {
        proxy_pass http://authentication/users/login/;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade; 
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade; 
    }

    location /users {
        auth_request /auth;
        auth_request_set $auth_status $upstream_status;
        proxy_pass http://authentication/users/;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade; 
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    location = /auth {
        internal;
        proxy_pass http://authentication/auth/;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Real-Ip $remote_addr;
        proxy_set_header Authorization $http_authorization;
    }

    error_page  404  /404.html;
        location = /40x.html {
    }

    error_page  500 502 503 504 /50x.html;
        location = /50x.html {
    }
}
 

感谢帮手

1 个答案:

答案 0 :(得分:0)

解决方案

我终于想出了如何使用Nginx解决这个websocket-jwt问题。 有一些不好的建议:
  1. 使用基本的身份验证机制来交付令牌,这是不推荐的,并且在某些浏览器中也会删除。
  2. 利用websocket的协议(第二个参数),将令牌和“ proxy_set_header Sec-WebSocket-Protocol $ http_sec_websocket_protocol;”安全地传递给服务器。在Nginx上。 也不建议使用它,因为它有点像黑客。

最佳解决方案: 从Nginx删除以下行:“ auth_request / auth”,因为现在检查将在上游服务器(例如NodeJS)的升级路由内进行。

互动:

  1. 客户端发送“升级” + cookie(带有JWT)。
  2. Nginx通过使用以下命令将请求与jwt cookie一起传递:“ proxy_set_header Set-Cookie $ cookie_ [cookie name];”
  3. 现在,当请求到达NodeJS“升级”时:将GET子请求发送到/ auth以验证令牌。 (您可以使用“ node-fetch”库) 如果状态为200,则令牌已经过验证,您可以连接网络套接字,否则请关闭连接。

总结:我们已经看到了如何通过简单地将/ auth子请求从Nginx移到NodeJS服务来将Websocket与JWT身份验证结合在一起。另外,我们还必须接受接受升级的另一步。

这些是代码的主要部分:

NodeJS“升级”路线:

server.on("upgrade", function upgrade(req, sock, head) {
  const pathname = url.parse(req.url).pathname;
  if (pathname === "/ws/video") {
    fetch("http://localhost/auth/", {
      headers: {
        Authorization: req.headers.cookie.split("=")[1]
      }
    })
      .then(resp => {
        if (!resp.ok) { 
          return;
        }
      })
      .catch(err => console.log(err));

    wss.handleUpgrade(req, sock, head, function done(ws) {
      wss.emit("connection", ws, req);
    });
  } else {
    sock.destroy();
  }
});

Nginx: #client

upstream client {
    server localhost:3000;
}

upstream authentication {
    server localhost:4000; 
}

upstream rtserver {
    server localhost:6000;
}

server {
    listen        80;
    listen        [::]:80;
    server_name localhost;

    root /home/guy/Documents/workspace/project/public;

    location / {
        proxy_pass http://client/; 
    }

    location /sockjs-node {
        proxy_pass http://client/sockjs-node;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_cache_bypass $http_upgrade;
    }   
 
    location /ws/videos {    
        proxy_pass http://rtserver/ws/videos;
        proxy_set_header   X-Forwarded-For $remote_addr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'Upgrade';
        proxy_set_header Host $host; 

        proxy_set_header Set-Cookie $cookie_video;

        proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol;
        proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
        proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
        proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
    } 

    location = /users/login {
        proxy_pass http://authentication/users/login/;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade; 
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade; 
    }

    location /users {
        auth_request /auth;
        auth_request_set $auth_status $upstream_status;
        proxy_pass http://authentication/users/;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade; 
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    location = /auth {
        internal;
        proxy_pass http://authentication/auth/;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Real-Ip $remote_addr;
        proxy_set_header Authorization $http_authorization;
    }

    error_page  404  /404.html;
        location = /40x.html {
    }

    error_page  500 502 503 504 /50x.html;
        location = /50x.html {
    }
}