如何與 Passport.js 搭配使用
讓我們從一個基本的應用程式開始
- CommonJS
- ES 模組
- TypeScript
const express = require("express");
const { createServer } = require("node:http");
const { Server } = require("socket.io");
const session = require("express-session");
const bodyParser = require("body-parser");
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const { join } = require("node:path");
const port = process.env.PORT || 3000;
const app = express();
const httpServer = createServer(app);
const sessionMiddleware = session({
secret: "changeit",
resave: true,
saveUninitialized: true,
});
app.use(sessionMiddleware);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.session());
app.get("/", (req, res) => {
if (!req.user) {
return res.redirect("/login");
}
res.sendFile(join(__dirname, "index.html"));
});
app.get("/login", (req, res) => {
if (req.user) {
return res.redirect("/");
}
res.sendFile(join(__dirname, "login.html"));
});
app.post(
"/login",
passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/",
}),
);
passport.use(
new LocalStrategy((username, password, done) => {
if (username === "john" && password === "changeit") {
console.log("authentication OK");
return done(null, { id: 1, username });
} else {
console.log("wrong credentials");
return done(null, false);
}
}),
);
passport.serializeUser((user, cb) => {
console.log(`serializeUser ${user.id}`);
cb(null, user);
});
passport.deserializeUser((user, cb) => {
console.log(`deserializeUser ${user.id}`);
cb(null, user);
});
const io = new Server(httpServer);
httpServer.listen(port, () => {
console.log(`application is running at: http://localhost:${port}`);
});
import express from "express";
import { createServer } from "http";
import { Server } from "socket.io";
import session from "express-session";
import bodyParser from "body-parser";
import passport from "passport";
import { Strategy as LocalStrategy } from "passport-local";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const port = process.env.PORT || 3000;
const app = express();
const httpServer = createServer(app);
const sessionMiddleware = session({
secret: "changeit",
resave: true,
saveUninitialized: true,
});
app.use(sessionMiddleware);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.session());
const __dirname = dirname(fileURLToPath(import.meta.url));
app.get("/", (req, res) => {
if (!req.user) {
return res.redirect("/login");
}
res.sendFile(join(__dirname, "index.html"));
});
app.get("/login", (req, res) => {
if (req.user) {
return res.redirect("/");
}
res.sendFile(join(__dirname, "login.html"));
});
app.post(
"/login",
passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/",
}),
);
app.post("/logout", (req, res) => {
const sessionId = req.session.id;
req.session.destroy(() => {
// disconnect all Socket.IO connections linked to this session ID
io.to(`session:${sessionId}`).disconnectSockets();
res.status(204).end();
});
});
passport.use(
new LocalStrategy((username, password, done) => {
if (username === "john" && password === "changeit") {
console.log("authentication OK");
return done(null, { id: 1, username });
} else {
console.log("wrong credentials");
return done(null, false);
}
}),
);
passport.serializeUser((user, cb) => {
console.log(`serializeUser ${user.id}`);
cb(null, user);
});
passport.deserializeUser((user, cb) => {
console.log(`deserializeUser ${user.id}`);
cb(null, user);
});
const io = new Server(httpServer);
httpServer.listen(port, () => {
console.log(`application is running at: http://localhost:${port}`);
});
import express = require("express");
import { createServer } from "http";
import { Server } from "socket.io";
import session from "express-session";
import { type Request, type Response } from "express";
import bodyParser = require("body-parser");
import passport = require("passport");
import { Strategy as LocalStrategy } from "passport-local";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
declare global {
namespace Express {
interface User {
id: number;
username: string;
}
}
}
const port = process.env.PORT || 3000;
const app = express();
const httpServer = createServer(app);
const sessionMiddleware = session({
secret: "changeit",
resave: true,
saveUninitialized: true,
});
app.use(sessionMiddleware);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
app.use(passport.session());
const __dirname = dirname(fileURLToPath(import.meta.url));
app.get("/", (req, res) => {
if (!req.user) {
return res.redirect("/login");
}
res.sendFile(join(__dirname, "index.html"));
});
app.get("/login", (req, res) => {
if (req.user) {
return res.redirect("/");
}
res.sendFile(join(__dirname, "login.html"));
});
app.post(
"/login",
passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/",
}),
);
app.post("/logout", (req, res) => {
const sessionId = req.session.id;
req.session.destroy(() => {
// disconnect all Socket.IO connections linked to this session ID
io.to(`session:${sessionId}`).disconnectSockets();
res.status(204).end();
});
});
passport.use(
new LocalStrategy((username, password, done) => {
if (username === "john" && password === "changeit") {
console.log("authentication OK");
return done(null, { id: 1, username });
} else {
console.log("wrong credentials");
return done(null, false);
}
}),
);
passport.serializeUser((user, cb) => {
console.log(`serializeUser ${user.id}`);
cb(null, user);
});
passport.deserializeUser((user: Express.User, cb) => {
console.log(`deserializeUser ${user.id}`);
cb(null, user);
});
const io = new Server(httpServer);
httpServer.listen(port, () => {
console.log(`application is running at: http://localhost:${port}`);
});
備註
您需要這些額外的類型
npm install @types/express @types/express-session @types/passport @types/passport-local
分享使用者內容
使用者內容可透過呼叫與 Socket.IO 伺服器分享
function onlyForHandshake(middleware) {
return (req, res, next) => {
const isHandshake = req._query.sid === undefined;
if (isHandshake) {
middleware(req, res, next);
} else {
next();
}
};
}
io.engine.use(onlyForHandshake(sessionMiddleware));
io.engine.use(onlyForHandshake(passport.session()));
io.engine.use(
onlyForHandshake((req, res, next) => {
if (req.user) {
next();
} else {
res.writeHead(401);
res.end();
}
}),
);
以下是發生的事情
express-session
中介軟體從 cookie 中擷取會話內容passport
中介軟體從會話中擷取使用者內容- 最後,如果找到使用者內容,則驗證握手
您現在可以存取 user
物件
io.on("connection", (socket) => {
const user = socket.request.user;
});
使用使用者 ID
您可以使用使用者 ID 在 Express 和 Socket.IO 之間建立連結
io.on("connection", (socket) => {
const userId = socket.request.user.id;
// the user ID is used as a room
socket.join(`user:${userId}`);
});
這讓您可以輕鬆地向特定使用者的所有連線廣播事件
io.to(`user:${userId}`).emit("foo", "bar");
您也可以檢查使用者目前是否已連線
const sockets = await io.in(`user:${userId}`).fetchSockets();
const isUserConnected = sockets.length > 0;
這就是與 Passport.js 的相容性。感謝您的閱讀!
完整的範例可以在 此處 找到。