Express.js,如何将jwt cookie传给Socket.io?

Express.js, how to pass jwt cookie to Socket.io?

我有一个登录路由,最终会创建一个名为 access_token 的 jwt cookie。登录后,客户端将收到此 cookie 并将在每次请求时发送它。但是我没有找到将此 cookie 传递给 Socket.io 的方法。

服务器端登录路由:

const login = async (req, res) => {
      const {email, password} = req.body
      const user = await UserModel.findOne({email})
      const isMatch = await user.checkPassword(password)
      if (isMatch) {
        const userToken = JwtService.createToken(user.id)
        return res.cookie("access_token", userToken, {
            httpOnly: true,
            secure: process.env.NODE_ENV === "production"
        }).status(200).json({user:user.toJSON(),message: 'login success'})

    }
 }

套接字:

this.io = new socketio.Server(expressServer, {cors: {origin: 'http://localhost:3000'}})
    this.io.use((socket,next)=>{
        console.log(socket.handshake.headers.cookie); // undefiend
        next()
    })

客户:

  this.socket = socketIOClient(process.env.SOCKET_BASE_URL, {auth: {userId}});

服务器:

         import express, {RequestHandler} from 'express';
        import http from 'http'
        import cookieParser from "cookie-parser"
        import cors from 'cors';
        import {router} from '@router';
        import dotenv from 'dotenv'
        import mongoose from 'mongoose';
        import {SocketService} from "@services";
        
        const expressApp = express();
        
        const port = process.env.PORT || 3001;
        dotenv.config()
        
        expressApp.use(cors({
            origin: true,
            credentials: true
        }));
        expressApp.use(express.json() as RequestHandler);
        expressApp.use(cookieParser());
        expressApp.use('/', router)
        
        const httpServer = http.createServer(expressApp);
        new SocketService(httpServer)
        
        httpServer.listen(port, async () => {
            console.log(`server is listening on ${port}`);
            try {
               await mongoose.connect('mongodb://guess-it-mongo-dev:27017/guess-it', {connectTimeoutMS: 1000});
               console.log('connected to mongo server')
            } catch (e) {
                console.log(e);
            }
        });

1。解决方案

假设您只有一个 cookie,即您的 jwt,您可以像这样使用 socket 参数获取它:

const token = socket.handshake.headers.cookie.split("=")[1];

如果你有很多cookie,你需要使用一些cookie解析器并给它socket.handshake.headers.cookie来解析它,例如:

function getCookie(cName) {
   const name = cName + "=";
   const cDecoded = decodeURIComponent(socket.handshake.headers.cookie);
   const cArr = cDecoded.split(';');
   let res;
   cArr.forEach(val => {
      if (val.indexOf(name) === 0) res = val.substring(name.length);
      })
   return res;
}
const token = getCookie("jwt"); // if your token is called jwt.

2。疑难解答

如果给定的解决方案不适合您,请让您以这种方式设置您的应用程序和套接字,将它们作为单个服务器(随意更改端口):

const app = express();
const http = require("http");
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

server.listen(9000, () => {
   console.log("server runnig on port " + 9000);
});

并且客户端应该像这样连接(使用与服务器相同的端口):

import { io } from "socket.io-client";

io("http://localhost:9000/", {
      transports: ["websocket"],
    });

3。一个用例

比如在代码中 下面,我正在使用 middleware 验证每个连接:

io.use((socket, next) => {
const token = socket.handshake.headers.cookie.split("=")[1];
if (token) {
  jwt.verify(token, process.env.SECRET, (err, decodedToken) => {
    if (err) {
      next(new Error("invalid"));
    } else {
      User.findById(decodedToken.id, function (err, user) {
        if (err) {
          next(new Error("invalid"));
        } else {
          next();
        }
      });
    }
  });
} else {
  next(new Error("invalid"));
}
});

//if authentication is ok, the code below will get executed 
io.on("connection", (socket) => {
  // do things
})