feat:token验证

This commit is contained in:
2025-11-01 19:20:48 +08:00
parent 6facd5f6cc
commit 78f7995d8a
7 changed files with 137 additions and 18 deletions

View File

@@ -2,5 +2,12 @@
"express":{ "express":{
"port": 3000, "port": 3000,
"jwtSecret": "" "jwtSecret": ""
},
"pgsql":{
"host": "localhost",
"port": 5432,
"user": "postgres",
"password": "<PASSWORD>",
"database": "postgres"
} }
} }

View File

@@ -17,11 +17,13 @@
"@tsconfig/node22": "^22.0.2", "@tsconfig/node22": "^22.0.2",
"@types/express": "^5.0.5", "@types/express": "^5.0.5",
"@types/jsonwebtoken": "^9.0.10", "@types/jsonwebtoken": "^9.0.10",
"@types/pg": "^8.15.6",
"typescript": "^5.9.3" "typescript": "^5.9.3"
}, },
"dependencies": { "dependencies": {
"express": "^5.1.0", "express": "^5.1.0",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"pg": "^8.16.3",
"socket.io": "^4.8.1" "socket.io": "^4.8.1"
} }
} }

View File

@@ -5,12 +5,26 @@ export interface Iconfig {
port: number; port: number;
jwtSecret: string; jwtSecret: string;
} }
pgsql: {
host: string;
port: number;
user: string;
password: string;
database: string;
}
} }
const defaultConfig = { const defaultConfig = {
"express":{ "express":{
"port": 3000, "port": 3000,
"jwtSecret": "JwtTianpaoMasterEasyTier000" "jwtSecret": "JwtTianpaoMasterEasyTier000"
},
"pgsql":{
"host": "localhost",
"port": 5432,
"user": "root",
"password": "root",
"database": "easytier"
} }
} }

View File

@@ -1,16 +1,89 @@
import { Request,Response,NextFunction } from "express"; import type{ Request, Response, NextFunction } from "express";
import crypto from "node:crypto";
import sql from "../utils/pgsql.js";
import config from "../config.js";
import { Jwt } from "../utils/jwt.js";
import { type JwtPayload } from "jsonwebtoken";
const clusters:string[] = []
export class AuthMiddleware { export class AuthMiddleware {
static Challenge(req: Request, res: Response, next: NextFunction) {
if (!req.headers["user-agent"]?.includes("OpenEasyTier")) {
res.status(403).send()
return;
}
const id = req.query.clusterId;
if (!id) {
res.status(401).json({
message: "where is your clusterId?",
});
return;
}
static Challenge(req: Request, res: Response, next: NextFunction) { if(clusters.includes(id.toString())){
const id = req.query.clusterId next(); //缓存命中直接进入下一步
if (!id){ return;
res.status(401).json({ }
message: "where is your clusterId?"
}) sql.query(`SELECT "cluster_id" FROM cluster WHERE "cluster_id" = $1`,[id]).then((result) => {
if (result.rows.length == 0) {
res.status(401).json({
message: "clusterId not found",
});
return;
}else{
clusters.push(id.toString())
}
next(); //Latest
return;
});
}
static Token(req: Request, res: Response, next: NextFunction) {
if (!req.headers["user-agent"]?.includes("OpenEasyTier")) {
res.status(403).send();
return;
}
const body = req.body;
if (
!body.clusterId &&
((!body.challenge && !body.signature) || !body.token)
) {
res.status(401).json({
message: "parameter miss",
});
}
sql.query(`SELECT "cluster_id" FROM cluster WHERE "cluster_id" = $1`,[body.clusterId]).then((result) => {
if (result.rows.length == 0) {
res.status(401).json({
message: "clusterId not found",
});
return;
}
if (body.signature) { //校验签名(如果有)
const sign = crypto.createHmac("sha256", config.express.jwtSecret).update(body.challenge).digest("hex").toLowerCase();
if(body.signature != sign){
res.status(403).json({
message: "signature not match",
});
return; return;
} }
next() //Latest
} }
if (body.token) { //校验token如果有
const jwt_token = Jwt.verify(body.token.replace("Bearer ","")) as JwtPayload as { clusterId: string };
if(jwt_token.clusterId != body.clusterId){
res.status(403).json({
message: "token not match",
});
return;
}
}
next(); //next function~
});
}
} }

View File

@@ -1,10 +1,24 @@
import { Router } from "express"; import { Router } from "express";
import { AuthMiddleware } from "../middleware/Auth.js"; import { AuthMiddleware } from "../middleware/Auth.js";
import { Jwt } from "../utils/jwt.js";
const router = Router(); const router = Router();
router.post('/challenge',AuthMiddleware.Challenge,(req,res)=>{ router.get("/challenge", AuthMiddleware.Challenge, (req, res) => {
const clusterId = req.query.clusterId;
const challenge = Jwt.sign({ clusterId });
res.json({
challenge:`Bearer ${challenge}`,
});
});
}) router.post("/token", AuthMiddleware.Token, (req, res) => {
const body = req.body;
const token = `Bearer ${Jwt.sign({ clusterId: body.clusterId })}`;
res.json({
token,
ttl: 1000 * 60 * 60 * 24, // TTL24小时
});
});
export default router; export default router;

View File

@@ -1,15 +1,18 @@
import jsonwebtoken from 'jsonwebtoken'; import jsonwebtoken from 'jsonwebtoken';
import config from '../config.js'; import config from '../config.js';
export class Jwt { export class Jwt {
static sign(payload: object){ static sign(payload: object,expiresIn: number = 60 * 60 * 24){
return jsonwebtoken.sign(payload,config.express.jwtSecret,{ return jsonwebtoken.sign(payload,config.express.jwtSecret,{
expiresIn: 60 * 60 * 24, // 24 hours expiresIn: expiresIn, // 24 hours
algorithm: "HS256" algorithm: "HS256",
issuer: "OpenEasyTierMaster"
}) })
} }
static verify(token: string) { static verify(token: string) {
return jsonwebtoken.verify(token,config.express.jwtSecret) return jsonwebtoken.verify(token,config.express.jwtSecret,{
issuer: "OpenEasyTierMaster"
})
} }
} }

6
src/utils/pgsql.ts Normal file
View File

@@ -0,0 +1,6 @@
import { Pool } from "pg";
import config from "../config.js";
const pool = new Pool(config.pgsql);
export default pool;