|
|
@@ -0,0 +1,200 @@
|
|
|
+import Connect from 'connect';
|
|
|
+import { ServerResponse } from 'node:http';
|
|
|
+import https from 'https';
|
|
|
+import { parse as parseCookie } from 'cookie';
|
|
|
+import { sendReLogin } from '../SSE/server';
|
|
|
+
|
|
|
+import casdoorServer from 'casdoor-nodejs-sdk';
|
|
|
+import {
|
|
|
+ ServerUrl,
|
|
|
+ ClientId,
|
|
|
+ SecretKey,
|
|
|
+ Certificate,
|
|
|
+ AppName,
|
|
|
+ OrganizationName
|
|
|
+} from './config';
|
|
|
+
|
|
|
+const sdk = new casdoorServer.SDK({
|
|
|
+ endpoint: ServerUrl,
|
|
|
+ clientId: ClientId,
|
|
|
+ clientSecret: SecretKey,
|
|
|
+ certificate: Certificate,
|
|
|
+ orgName: OrganizationName,
|
|
|
+ appName: AppName
|
|
|
+}, {
|
|
|
+ httpsAgent: new https.Agent({ rejectUnauthorized: false })
|
|
|
+})
|
|
|
+
|
|
|
+// 未授权响应
|
|
|
+function resUnauthorized(res: ServerResponse<Connect.IncomingMessage>, cookies?: Record<string, string | undefined>) {
|
|
|
+ res.statusCode = 401; // Unauthorized
|
|
|
+ res.setHeader('Content-Type', 'text/plain')
|
|
|
+ res.end('Unauthorized')
|
|
|
+
|
|
|
+ if (cookies) {
|
|
|
+ const session_id = cookies['session_id']
|
|
|
+ if (session_id) {
|
|
|
+ // 重新登录
|
|
|
+ setTimeout(() => {
|
|
|
+ sendReLogin(session_id);
|
|
|
+ }, 3000);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理登录请求
|
|
|
+async function handleSignin(url: URL): Promise<{ access_token: string; refresh_token: string; } | undefined> {
|
|
|
+ const code = url.searchParams.get('code');
|
|
|
+ const state = url.searchParams.get('state');
|
|
|
+ if (code && state) {
|
|
|
+ const token = await sdk.getAuthToken(code);
|
|
|
+ return token
|
|
|
+ }
|
|
|
+
|
|
|
+ return undefined
|
|
|
+}
|
|
|
+
|
|
|
+// 验证Token
|
|
|
+function verifyToken(token: string): boolean {
|
|
|
+ try {
|
|
|
+ sdk.parseJwtToken(token);
|
|
|
+ return true;
|
|
|
+ } catch (err) {
|
|
|
+ console.error("token verify failed.");
|
|
|
+ console.error(err);
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+// 刷新Token
|
|
|
+async function refreshToken(refresh_token: string) {
|
|
|
+ const tokenGroup = await sdk.refreshToken(refresh_token).catch(err => {
|
|
|
+ console.error("refresh token failed.")
|
|
|
+ console.error(err)
|
|
|
+ return undefined
|
|
|
+ })
|
|
|
+
|
|
|
+ return tokenGroup;
|
|
|
+}
|
|
|
+
|
|
|
+export default async function AuthMid(req: Connect.IncomingMessage, res: ServerResponse<Connect.IncomingMessage>, next: Connect.NextFunction) {
|
|
|
+ const BaseUrl = process.env.BASE_URL
|
|
|
+
|
|
|
+ if (req.url) {
|
|
|
+ const url = new URL(req.url, "https://raspberrypi.dev");
|
|
|
+
|
|
|
+ // 请求保护资源,验证JWT
|
|
|
+ if (!url.pathname.startsWith(BaseUrl + 'casdoor') && url.pathname.endsWith('.md')) {
|
|
|
+ console.log("verify JWT")
|
|
|
+ const cookieStr = req.headers.cookie
|
|
|
+ if (!cookieStr) {
|
|
|
+ resUnauthorized(res);
|
|
|
+ return
|
|
|
+ } else {
|
|
|
+ const cookies = parseCookie(cookieStr);
|
|
|
+ console.log(cookies)
|
|
|
+ const token = cookies['token'];
|
|
|
+ const refresh_token = cookies['refresh_token'];
|
|
|
+
|
|
|
+ // 验证Token
|
|
|
+ if (token && verifyToken(token)) {
|
|
|
+ // 验证成功
|
|
|
+ await next();
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证刷新Token
|
|
|
+ if (refresh_token && verifyToken(refresh_token)) {
|
|
|
+ // 刷新Token
|
|
|
+ const tokenGroup = await refreshToken(refresh_token);
|
|
|
+ if (tokenGroup) {
|
|
|
+ // 刷新成功,设置token
|
|
|
+ res.setHeader('Set-Cookie', [
|
|
|
+ `token=${tokenGroup.access_token}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`,
|
|
|
+ `refresh_token=${tokenGroup.refresh_token}; HttpOnly; Path=/; SameSite=Strict`
|
|
|
+ ]);
|
|
|
+ await next();
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ resUnauthorized(res, cookies);
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理登录查询
|
|
|
+ if (url.pathname.startsWith(BaseUrl + 'api/islogin')) {
|
|
|
+ const cookieStr = req.headers.cookie
|
|
|
+ if (cookieStr) {
|
|
|
+ const cookies = parseCookie(cookieStr);
|
|
|
+ const token = cookies['token'];
|
|
|
+ const refresh_token = cookies['refresh_token'];
|
|
|
+
|
|
|
+ // 验证Token
|
|
|
+ if (token && verifyToken(token)) {
|
|
|
+ // 验证成功
|
|
|
+ res.statusCode = 200;
|
|
|
+ res.setHeader('Content-Type', 'text/plain')
|
|
|
+ res.end(token)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证刷新Token
|
|
|
+ if (refresh_token && verifyToken(refresh_token)) {
|
|
|
+ // 刷新Token
|
|
|
+ const tokenGroup = await refreshToken(refresh_token);
|
|
|
+ if (tokenGroup) {
|
|
|
+ // 刷新成功,设置token
|
|
|
+ res.setHeader('Set-Cookie', [
|
|
|
+ `token=${tokenGroup.access_token}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`,
|
|
|
+ `refresh_token=${tokenGroup.refresh_token}; HttpOnly; Path=/; SameSite=Strict`
|
|
|
+ ]);
|
|
|
+ res.statusCode = 200;
|
|
|
+ res.setHeader('Content-Type', 'text/plain')
|
|
|
+ res.end(token)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ resUnauthorized(res);
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理登录请求
|
|
|
+ if (url.pathname.startsWith(BaseUrl + 'api/signin')) {
|
|
|
+ const signinToken = await handleSignin(url).catch((err) => {
|
|
|
+ console.log("handleSignin: ", err)
|
|
|
+ });
|
|
|
+ console.log("signinToken: ", signinToken)
|
|
|
+ if (signinToken) {
|
|
|
+ res.setHeader('Set-Cookie', [
|
|
|
+ `token=${signinToken.access_token}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`,
|
|
|
+ `refresh_token=${signinToken.refresh_token}; HttpOnly; Path=/; SameSite=Strict`
|
|
|
+ ])
|
|
|
+ res.statusCode = 200;
|
|
|
+ res.setHeader('Content-Type', 'application/json')
|
|
|
+ res.end(JSON.stringify(signinToken))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理登录请求
|
|
|
+ if (url.pathname.startsWith(BaseUrl + 'api/exit')) {
|
|
|
+ console.log("exit")
|
|
|
+ // 清除token
|
|
|
+ res.setHeader('Set-Cookie', [
|
|
|
+ `token=; HttpOnly; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Strict`,
|
|
|
+ `refresh_token=; HttpOnly; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Strict`
|
|
|
+ ])
|
|
|
+ res.statusCode = 200;
|
|
|
+ res.setHeader('Content-Type', 'text/plain')
|
|
|
+ res.end("Exit successful.")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 继续执行下一个中间件
|
|
|
+ await next();
|
|
|
+}
|