如何使用 JSON 網路代幣
資訊
JSON 網路代幣 (JWT) 是一個開放標準 (RFC 7519),定義了一種簡潔且自給自足的方式,可以將資訊以 JSON 物件的形式安全地傳輸到各方之間。此資訊可以被驗證和信任,因為它是經過數位簽章的。
由於負擔小,且能夠輕鬆地在不同網域間使用,因此它經常被用於驗證。
更多資訊 在此。
讓我們從一個基本的應用程式開始
- CommonJS
- ES 模組
- TypeScript
const express = require("express");
const { createServer } = require("node:http");
const { join } = require("node:path");
const passport = require("passport");
const passportJwt = require("passport-jwt");
const JwtStrategy = passportJwt.Strategy;
const ExtractJwt = passportJwt.ExtractJwt;
const bodyParser = require("body-parser");
const { Server } = require("socket.io");
const jwt = require("jsonwebtoken");
const port = process.env.PORT || 3000;
const jwtSecret = "Mys3cr3t";
const app = express();
const httpServer = createServer(app);
app.use(bodyParser.json());
app.get("/", (req, res) => {
res.sendFile(join(__dirname, "index.html"));
});
app.get(
"/self",
passport.authenticate("jwt", { session: false }),
(req, res) => {
if (req.user) {
res.send(req.user);
} else {
res.status(401).end();
}
},
);
app.post("/login", (req, res) => {
if (req.body.username === "john" && req.body.password === "changeit") {
console.log("authentication OK");
const user = {
id: 1,
username: "john",
};
const token = jwt.sign(
{
data: user,
},
jwtSecret,
{
issuer: "accounts.examplesoft.com",
audience: "yoursite.net",
expiresIn: "1h",
},
);
res.json({ token });
} else {
console.log("wrong credentials");
res.status(401).end();
}
});
const jwtDecodeOptions = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: jwtSecret,
issuer: "accounts.examplesoft.com",
audience: "yoursite.net",
};
passport.use(
new JwtStrategy(jwtDecodeOptions, (payload, done) => {
return done(null, payload.data);
}),
);
const io = new Server(httpServer);
httpServer.listen(port, () => {
console.log(`application is running at: http://localhost:${port}`);
});
import express from "express";
import { createServer } from "node:http";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import passport from "passport";
import { Strategy as JwtStrategy, ExtractJwt } from "passport-jwt";
import bodyParser from "body-parser";
import { Server } from "socket.io";
import jwt from "jsonwebtoken";
const port = process.env.PORT || 3000;
const jwtSecret = "Mys3cr3t";
const app = express();
const httpServer = createServer(app);
app.use(bodyParser.json());
const __dirname = dirname(fileURLToPath(import.meta.url));
app.get("/", (req, res) => {
res.sendFile(join(__dirname, "index.html"));
});
app.get(
"/self",
passport.authenticate("jwt", { session: false }),
(req, res) => {
if (req.user) {
res.send(req.user);
} else {
res.status(401).end();
}
},
);
app.post("/login", (req, res) => {
if (req.body.username === "john" && req.body.password === "changeit") {
console.log("authentication OK");
const user = {
id: 1,
username: "john",
};
const token = jwt.sign(
{
data: user,
},
jwtSecret,
{
issuer: "accounts.examplesoft.com",
audience: "yoursite.net",
expiresIn: "1h",
},
);
res.json({ token });
} else {
console.log("wrong credentials");
res.status(401).end();
}
});
const jwtDecodeOptions = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: jwtSecret,
issuer: "accounts.examplesoft.com",
audience: "yoursite.net",
};
passport.use(
new JwtStrategy(jwtDecodeOptions, (payload, done) => {
return done(null, payload.data);
}),
);
const io = new Server(httpServer);
httpServer.listen(port, () => {
console.log(`application is running at: http://localhost:${port}`);
});
import express from "express";
import { type Request, type Response } from "express";
import { createServer } from "node:http";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import passport from "passport";
import { Strategy as JwtStrategy, ExtractJwt } from "passport-jwt";
import bodyParser from "body-parser";
import { Server } from "socket.io";
import jwt from "jsonwebtoken";
declare global {
namespace Express {
interface User {
id: number;
username: string;
}
}
}
const port = process.env.PORT || 3000;
const jwtSecret = "Mys3cr3t";
const app = express();
const httpServer = createServer(app);
app.use(bodyParser.json());
const __dirname = dirname(fileURLToPath(import.meta.url));
app.get("/", (req, res) => {
res.sendFile(join(__dirname, "index.html"));
});
app.get(
"/self",
passport.authenticate("jwt", { session: false }),
(req, res) => {
if (req.user) {
res.send(req.user);
} else {
res.status(401).end();
}
},
);
app.post("/login", (req, res) => {
if (req.body.username === "john" && req.body.password === "changeit") {
console.log("authentication OK");
const user = {
id: 1,
username: "john",
};
const token = jwt.sign(
{
data: user,
},
jwtSecret,
{
issuer: "accounts.examplesoft.com",
audience: "yoursite.net",
expiresIn: "1h",
},
);
res.json({ token });
} else {
console.log("wrong credentials");
res.status(401).end();
}
});
const jwtDecodeOptions = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: jwtSecret,
issuer: "accounts.examplesoft.com",
audience: "yoursite.net",
};
passport.use(
new JwtStrategy(jwtDecodeOptions, (payload, done) => {
return done(null, payload.data);
}),
);
const io = new Server(httpServer);
httpServer.listen(port, () => {
console.log(`application is running at: http://localhost:${port}`);
});
備註
您需要這些額外的類型
npm install @types/express @types/jsonwebtoken @types/passport @types/passport-jwt
備註
在此範例中,我們手動在 /login
處理常式中建立代幣,但它可能來自您自己的應用程式中的其他地方。
在用戶端,代幣包含在 Authorization
標頭中
const socket = io({
extraHeaders: {
authorization: `bearer ${myToken}`
}
});
共用使用者內容
可透過呼叫來與 Socket.IO 伺服器共用使用者內容
io.engine.use((req, res, next) => {
const isHandshake = req._query.sid === undefined;
if (isHandshake) {
passport.authenticate("jwt", { session: false })(req, res, next);
} else {
next();
}
});
isHandshake
檢查可確保中介軟體只套用於該階段的第一個 HTTP 要求。
您現在可以存取 user
物件
io.on("connection", (socket) => {
const user = socket.request.user;
});
手動剖析
在上述範例中,我們使用 passport-jwt
套件,但您絕對可以使用 jsonwebtoken
套件手動驗證載入代幣
io.engine.use((req, res, next) => {
const isHandshake = req._query.sid === undefined;
if (!isHandshake) {
return next();
}
const header = req.headers["authorization"];
if (!header) {
return next(new Error("no token"));
}
if (!header.startsWith("bearer ")) {
return next(new Error("invalid token"));
}
const token = header.substring(7);
jwt.verify(token, jwtSecret, (err, decoded) => {
if (err) {
return next(new Error("invalid token"));
}
req.user = decoded.data;
next();
});
});
使用使用者 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;
這就是與 JSON Web Tokens 的相容性。感謝您的閱讀!
完整的範例可以在 這裡 找到。
- CommonJS
- ES 模組
- TypeScript
您可以在以下位置直接於瀏覽器中執行此範例
您可以在以下位置直接於瀏覽器中執行此範例
您可以在以下位置直接於瀏覽器中執行此範例