feat:token验证
This commit is contained in:
@@ -2,5 +2,12 @@
|
|||||||
"express":{
|
"express":{
|
||||||
"port": 3000,
|
"port": 3000,
|
||||||
"jwtSecret": ""
|
"jwtSecret": ""
|
||||||
|
},
|
||||||
|
"pgsql":{
|
||||||
|
"host": "localhost",
|
||||||
|
"port": 5432,
|
||||||
|
"user": "postgres",
|
||||||
|
"password": "<PASSWORD>",
|
||||||
|
"database": "postgres"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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~
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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, // TTL:24小时
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -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
6
src/utils/pgsql.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { Pool } from "pg";
|
||||||
|
import config from "../config.js";
|
||||||
|
|
||||||
|
const pool = new Pool(config.pgsql);
|
||||||
|
|
||||||
|
export default pool;
|
||||||
Reference in New Issue
Block a user