隨著無線網絡的普及,網絡攻擊風險也日益嚴峻。本項目旨在構建一個實時監測、智能識別、高效防護的無線網絡安全平臺,通過結合前后端技術與安全算法,實現對常見攻擊行為的有效監控和防御。
一、項目簡介與功能目的
本系統是一款基于 React 前端 + Python 后端 架構開發的無線網絡入侵檢測系統 V1.0,專注于無線網絡安全領域的可視化平臺建設。平臺集成了 DDoS 防御、異常流量識別、漏洞掃描、日志分析、黑名單管理等核心功能模塊,幫助用戶從設備層到數據層全面掌握網絡安全狀態。
🧾 功能目標:
-
實時監測無線網絡活動與異常行為
-
提供清晰、可交互的數據可視化展示
-
輔助用戶快速定位安全隱患與潛在攻擊源
-
支持日志溯源與安全報告生成
-
適用于校園網絡、企業內網、實驗室網絡等應用場景
二、背景分析:為什么要做這類系統?
在現代辦公和日常生活中,無線網絡已成為信息傳輸的重要載體。然而其開放性與可被感知的特性,也使其暴露在更多攻擊風險下:
-
🕵? DDoS洪水攻擊、ARP欺騙、端口掃描等日益頻繁
-
🔓 企業常規防火墻難以精準應對新型攻擊行為
-
?? 安全日志分析、漏洞感知能力在傳統系統中缺失
因此,本項目以“智能監測 + 可視化感知 + 實時響應”為核心目標,力圖構建一個可控、可查、可溯源的無線網絡防護系統。
三、項目整體架構說明
項目采用 前后端分離 架構,結合現代 Web 技術與網絡安全分析框架:
🖥 前端:React + Chart.js + Recharts
-
構建響應式界面
-
實時展示網絡流量、入侵事件、設備狀態等
-
使用 Socket.IO 與后端通信,實現實時數據同步
🔧 后端:Python + Flask + Scapy
-
提供 REST API 接口
-
網絡數據包捕獲與解析(基于 scapy)
-
異常流量檢測、黑名單管理、漏洞分析
-
使用機器學習/規則引擎實現行為識別
🗂 數據存儲:MySQL
-
存儲用戶信息、網絡日志、設備數據等
-
支持高效查詢與日志導出
🔐 安全機制:
-
HTTPS 傳輸加密
-
登錄認證 + 權限分級控制
-
日志記錄與操作審計
四、項目代碼結構
📁 前端目錄結構(React)
wireless-security-platform/
├── public/ # 靜態資源
├── src/
│ ├── components/ # 功能組件(Dashboard/NetFilter/PDF等)
│ ├── App.jsx # 主組件
│ └── styles.css # 全局樣式
├── package.json # 項目依賴
└── README.md
📁 后端目錄結構(Python)
backend/
├── app/ # 主應用代碼
│ └── utils/ # 工具函數
├── run.py # 啟動入口
├── config.py # 配置文件
├── requirements.txt # 第三方依賴
├── traffic.json # 流量分析數據樣本
└── wsgi.py # WSGI 服務入口
五、核心功能模塊詳解
1?? 數據包嗅探器 & ARP欺騙檢測
-
實時抓包、統計包數量
-
支持分鐘級、秒級數據可視化展示
-
檢測是否存在 ARP 欺騙行為
import React, { useEffect, useRef, useState } from 'react';
import io from 'socket.io-client';
import { Chart, CategoryScale, LinearScale, LineController, LineElement, PointElement } from 'chart.js';
import './NetFilter.css';// 注冊必要的比例尺、控制器和元素
Chart.register(CategoryScale, LinearScale, LineController, LineElement, PointElement);const NetFilter = () => {const [isSniffing, setIsSniffing] = useState(false);const [filter, setFilter] = useState('All');const logDivRef = useRef(null);const cumulativeChartRef = useRef(null);const unitTimeChartRef = useRef(null);const socket = useRef(null);useEffect(() => {socket.current = io('http://127.0.0.1:5555', {transports: ['websocket', 'polling'],pingTimeout: 60000,pingInterval: 25000,withCredentials: true,reconnection: true,reconnectionAttempts: 5,reconnectionDelay: 3000,debug: true, // 啟用調試模式});socket.current.on('connect', () => {console.log('已經與后端建立了連接'); // 確保連接成功});socket.current.on('connect_error', (error) => {console.error('Connection error:', error); // 檢查連接錯誤});socket.current.on('disconnect', (reason) => {console.log('Disconnected:', reason);});// 確保事件監聽器在連接成功后立即注冊socket.current.on('packet', (log_entry) => {console.log('Received packet log:', log_entry); // 調試信息logDivRef.current.innerHTML += `<p>${log_entry}</p>`;logDivRef.current.scrollTop = logDivRef.current.scrollHeight;} );const cumulativeCtx = document.getElementById('cumulativeChart')?.getContext('2d');const unitTimeCtx = document.getElementById('unitTimeChart')?.getContext('2d');if (!cumulativeCtx || !unitTimeCtx) {console.error('Canvas context not found');return;}// 銷毀舊圖表if (cumulativeChartRef.current) {cumulativeChartRef.current.destroy();}if (unitTimeChartRef.current) {unitTimeChartRef.current.destroy();}// 初始化新圖表cumulativeChartRef.current = new Chart(cumulativeCtx, {type: 'line',data: {labels: [],datasets: [{label: '累積數據包數量',data: [],borderColor: '#00d2ff',fill: false}]},options: {scales: {x: {type: 'category',title: { display: true, text: '時間', color: 'white', font: { size: 16 } },ticks: { color: 'white', font: { size: 14 } }},y: {type: 'linear',title: { display: true, text: '數據包數量', color: 'white', font: { size: 16 } },beginAtZero: true,ticks: { color: 'white', font: { size: 14 } }}},plugins: {legend: { display: false },tooltip: { enabled: true }}}});unitTimeChartRef.current = new Chart(unitTimeCtx, {type: 'line',data: {labels: [],datasets: [{label: '單位時間數據包數量',data: [],borderColor: '#ff6384',fill: false}]},options: {scales: {x: {type: 'category',title: { display: true, text: '時間', color: 'white', font: { size: 16 } },ticks: { color: 'white', font: { size: 14 } }},y: {type: 'linear',title: { display: true, text: '數據包數量', color: 'white', font: { size: 16 } },beginAtZero: true,ticks: { color: 'white', font: { size: 14 } }}},plugins: {legend: { display: false },tooltip: { enabled: true }}}});socket.current.on('arp_alert', (alert) => {alert(alert);});socket.current.on('sniffing_status', (data) => {console.log('Sniffing status:', data);setIsSniffing(data.status === 'started');updateGuiLog(data.status === 'started' ? "[+] 抓包已開始" : "[!] 抓包已停止");});socket.current.on('packet_stats', (stats) => {if (cumulativeChartRef.current && unitTimeChartRef.current) {cumulativeChartRef.current.data.labels = stats.labels;cumulativeChartRef.current.data.datasets[0].data = stats.cumulative;cumulativeChartRef.current.update();unitTimeChartRef.current.data.labels = stats.labels;unitTimeChartRef.current.data.datasets[0].data = stats.unit_time;unitTimeChartRef.current.update();}});return () => {// 組件卸載時銷毀圖表并斷開連接if (cumulativeChartRef.current) {cumulativeChartRef.current.destroy();}if (unitTimeChartRef.current) {unitTimeChartRef.current.destroy();}if (socket.current) {socket.current.disconnect();}// 組件卸載時移除事件監聽器if (socket.current) {socket.current.off('packet_log');}};}, []);const handleStart = () => {if (socket.current) {console.log('正在開始對:', filter,'數據包信息進行記錄'); // 調試日志socket.current.emit('start_sniffing', { filter_option: filter }, (response) => {console.log('已向發送')if (response) {console.log('后端的響應為:', response); // 后端響應日志} else {console.error('后端無任何響應'); // 無響應日志}});setIsSniffing(true); // 更新 isSniffing 為 true} else {console.error('Socket is not connected');}};const handleStop = () => {if (socket.current) {console.log('已經停止監控'); // 調試日志socket.current.emit('stop_sniffing');setIsSniffing(false); // 更新 isSniffing 為 false} else {console.error('Socket is not connected');}};const updateGuiLog = (message) => {if (logDivRef.current) {logDivRef.current.innerHTML += `<p>${message}</p>`;logDivRef.current.scrollTop = logDivRef.current.scrollHeight;}};const handleFilterChange = (e) => {setFilter(e.target.value);if (isSniffing) {socket.current.emit('update_filter', e.target.value);updateGuiLog(`[+] 過濾器已更新為: ${e.target.value}`);}};const handleIntervalChange = (interval) => {socket.current.emit('set_interval', interval);};const handleFullscreen = (chartType) => {const chartContainer = chartType === 'cumulative' ? cumulativeChartRef.current.canvas.parentElement: unitTimeChartRef.current.canvas.parentElement;chartContainer.classList.add('fullscreen');addExitButton(chartContainer);};const addExitButton = (container) => {const exitButton = document.createElement('button');exitButton.className = 'exit-fullscreen';exitButton.innerText = '退出全屏';exitButton.addEventListener('click', () => {container.classList.remove('fullscreen');exitButton.remove();});container.appendChild(exitButton);};return (<div className="net-filter-container"><h1>數據包嗅探器 & ARP欺騙檢測</h1><div className="controls"><select id="filter" value={filter} onChange={handleFilterChange}><option value="All">全部</option><option value="ARP">ARP</option><option value="DNS">DNS</option><option value="TCP">TCP</option><option value="UDP">UDP</option><option value="HTTP">HTTP</option></select><button id="start" onClick={handleStart} disabled={isSniffing}>開始抓包</button><button id="stop" onClick={handleStop} disabled={!isSniffing}>停止抓包</button></div><div className={`status-indicator ${isSniffing ? 'active' : ''}`}><div className="dot"></div><span id="status-text">{isSniffing ? '抓包中...' : '抓包已停止'}</span></div><div id="log" ref={logDivRef}></div><div className="chart-container"><div className="chart-box"><h2>累積數據包數量</h2><div className="chart-controls"><button onClick={() => handleIntervalChange(60)}>每分鐘</button><button onClick={() => handleIntervalChange(300)}>每5分鐘</button><button onClick={() => handleFullscreen('cumulative')}>全屏顯示</button></div><canvas id="cumulativeChart" key="cumulativeChart"></canvas></div><div className="chart-box"><h2>單位時間數據包數量</h2><div className="chart-controls"><button onClick={() => handleIntervalChange(60)}>每分鐘</button><button onClick={() => handleIntervalChange(300)}>每5分鐘</button><button onClick={() => handleFullscreen('unitTime')}>全屏顯示</button></div><canvas id="unitTimeChart" key="unitTimeChart"></canvas></div></div></div>);
};export default NetFilter;
2?? 漏洞掃描模塊
-
一鍵啟動漏洞掃描流程
-
分類展示高風險/處理中/已修復漏洞
-
提供 CVE 編號、風險等級、修復建議等技術說明
-
自動生成漏洞檢測報告(PDF導出)
import React, { useState } from 'react';
import { FaBug, FaShieldAlt, FaChartBar, FaSearch, FaExclamationTriangle } from 'react-icons/fa';function VulnerabilityScan() {const [scanResults, setScanResults] = useState([{ id: 1, name: 'CVE-2023-1234', severity: 'high', description: '遠程代碼執行漏洞', status: '未修復' },{ id: 2, name: 'CVE-2023-5678', severity: 'medium', description: '權限提升漏洞', status: '已修復' },{ id: 3, name: 'CVE-2023-9101', severity: 'low', description: '信息泄露漏洞', status: '未修復' }]);const [selectedVulnerability, setSelectedVulnerability] = useState(null);const handleScan = () => {// 模擬掃描結果setScanResults([...scanResults,{ id: 4, name: 'CVE-2023-1122', severity: 'high', description: 'SQL注入漏洞', status: '未修復' }]);};return (<section className="vulnerability-scan-section"><h2><FaBug /> 漏洞掃描</h2><div className="scan-controls"><button className="scan-btn" onClick={handleScan}><FaSearch /><span>開始掃描</span></button></div><div className="scan-results"><div className="results-overview"><div className="overview-card"><div className="card-header"><FaExclamationTriangle /><h3>高風險漏洞</h3></div><div className="card-content"><span className="count">2</span><p>需要立即處理</p></div></div><div className="overview-card"><div className="card-header"><FaShieldAlt /><h3>已修復漏洞</h3></div><div className="card-content"><span className="count">1</span><p>已處理完成</p></div></div><div className="overview-card"><div className="card-header"><FaChartBar /><h3>總漏洞數</h3></div><div className="card-content"><span className="count">3</span><p>已檢測到</p></div></div></div><div className="results-details"><h3>漏洞詳情</h3><table className="vulnerabilities-table"><thead><tr><th>漏洞編號</th><th>嚴重程度</th><th>描述</th><th>狀態</th></tr></thead><tbody>{scanResults.map(vuln => (<tr key={vuln.id}className={`${vuln.severity} ${selectedVulnerability?.id === vuln.id ? 'selected' : ''}`}onClick={() => setSelectedVulnerability(vuln)}><td>{vuln.name}</td><td><span className={`severity-badge ${vuln.severity}`}>{vuln.severity === 'high' ? '高' : vuln.severity === 'medium' ? '中' : '低'}</span></td><td>{vuln.description}</td><td>{vuln.status}</td></tr>))}</tbody></table></div></div>{selectedVulnerability && (<div className="vulnerability-details"><h4>漏洞詳情:{selectedVulnerability.name}</h4><div className="details-content"><p><strong>嚴重程度:</strong><span className={`severity-badge ${selectedVulnerability.severity}`}>{selectedVulnerability.severity === 'high' ? '高' : selectedVulnerability.severity === 'medium' ? '中' : '低'}</span></p><p><strong>描述:</strong>{selectedVulnerability.description}</p><p><strong>狀態:</strong>{selectedVulnerability.status}</p><p><strong>建議:</strong>請及時更新相關軟件版本或應用安全補丁。</p></div></div>)}</section>);
}export default VulnerabilityScan;
3?? 黑名單管理與網絡狀態可視化
-
支持手動/自動添加惡意IP
-
核心網絡設備狀態實時監控(如路由器/交換機/服務器)
-
動態折線圖展示網絡流量趨勢
import React from 'react';
import { FaDesktop, FaBug, FaKey } from 'react-icons/fa';function HostVulnerability() {return (<section className="host-vulnerability-section"><h2>主機與漏洞概覽</h2><div className="overview-cards"><div className="card"><div className="card-header"><FaDesktop /><h3>主機發現</h3></div><div className="card-content"><div className="stat">24</div><p>最近掃描到的主機數量</p><button className="view-details-btn">查看詳情</button></div></div><div className="card"><div className="card-header"><FaBug /><h3>漏洞掃描</h3></div><div className="card-content"><div className="stat">7</div><p>最近檢測到的漏洞數量</p><div className="vulnerability-levels"><span className="level high">高: 2</span><span className="level medium">中: 3</span><span className="level low">低: 2</span></div><button className="view-details-btn">查看詳情</button></div></div><div className="card"><div className="card-header"><FaKey /><h3>弱密碼檢測</h3></div><div className="card-content"><div className="stat">5</div><p>檢測到的弱密碼設備數量</p><button className="view-details-btn">查看詳情</button></div></div></div></section>);
}export default HostVulnerability;
4?? 日志分析模塊
-
支持多主機日志對比(主機A / 主機B)
-
日志來源支持 MySQL / Nginx / Tomcat 等
-
時間范圍篩選、日志等級過濾、關鍵詞搜索
-
保留6個月歷史記錄,滿足安全審計需求
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import './Knowledge.css'; // 引入CSS文件// 使用styled-components定義樣式
const Loading = styled.div`margin-top: 10px;padding: 10px;border-radius: 4px;font-size: 14px;text-align: center;background-color: #f8f9fa;color: #666;
`;// 新增卡片樣式
const Card = styled.div`background: rgba(255, 255, 255, 0.8);border-radius: 20px;padding: 20px;margin: 15px 0;box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);backdrop-filter: blur(10px);border: 1px solid rgba(255, 255, 255, 0.18);transition: all 0.3s ease;&:hover {transform: translateY(-5px);box-shadow: 0 12px 40px 0 rgba(31, 38, 135, 0.25);}
`;// 日志解析器
const getLogParser = (source) => {const parsers = {mysql: (log) => ({timestamp: new Date(log.timestamp),message: log.message,level: log.level || 'info',source: 'MySQL'}),mongodb: (log) => ({timestamp: new Date(log.timestamp),message: log.message,level: log.level || 'info',source: 'MongoDB'}),tomcat: (log) => ({timestamp: new Date(log.timestamp),message: log.message,level: log.level || 'info',source: 'Tomcat'}),nginx: (log) => ({timestamp: new Date(log.timestamp),message: log.message,level: log.level || 'info',source: 'Nginx'}),kafka: (log) => ({timestamp: new Date(log.timestamp),message: log.message,level: log.level || 'info',source: 'Kafka'}),redis: (log) => ({timestamp: new Date(log.timestamp),message: log.message,level: log.level || 'info',source: 'Redis'})};return parsers[source] || (log => log);
};// 封裝模塊組件
const LogModule = ({ hostName, apiEndpoint }) => {const [logData, setLogData] = useState([]);const [filteredData, setFilteredData] = useState([]);const [searchTerm, setSearchTerm] = useState('');const [dateRange, setDateRange] = useState([null, null]);const [timeInterval, setTimeInterval] = useState('1h');const [isLoading, setIsLoading] = useState(false);const [selectedSources, setSelectedSources] = useState([]);// 中間件選項const middlewareOptions = [{ value: 'mysql', label: 'MySQL' },{ value: 'nginx', label: 'Nginx' },{ value: 'tomcat', label: 'Tomcat' },{ value: 'kafka', label: 'Kafka' },{ value: 'redis', label: 'Redis' }];// 根據時間間隔更新日期范圍const updateDateRange = (interval) => {const now = new Date();let startDate = new Date(now);switch (interval) {case '5m':startDate.setMinutes(now.getMinutes() - 5);break;case '15m':startDate.setMinutes(now.getMinutes() - 15);break;case '1h':startDate.setHours(now.getHours() - 1);break;case '6h':startDate.setHours(now.getHours() - 6);break;case '1d':startDate.setDate(now.getDate() - 1);break;case '1w':startDate.setDate(now.getDate() - 7);break;case '1mo':startDate.setMonth(now.getMonth() - 1);break;default:startDate = null;}setDateRange([startDate, now]);};// 處理時間間隔變化const handleTimeIntervalChange = (e) => {const newInterval = e.target.value;setTimeInterval(newInterval);updateDateRange(newInterval);};// 獲取日志數據useEffect(() => {const mockLogs = {mysql: [{ timestamp: new Date().toISOString(), message: '數據庫連接成功', level: 'info' },{ timestamp: new Date(Date.now() - 300000).toISOString(), message: '查詢執行時間過長', level: 'warning' },{ timestamp: new Date(Date.now() - 600000).toISOString(), message: '數據庫備份完成', level: 'info' }],nginx: [{ timestamp: new Date().toISOString(), message: '200 GET /index.html', level: 'info' },{ timestamp: new Date(Date.now() - 120000).toISOString(), message: '404 GET /nonexistent', level: 'error' },{ timestamp: new Date(Date.now() - 300000).toISOString(), message: '502 Bad Gateway', level: 'error' }],tomcat: [{ timestamp: new Date().toISOString(), message: '服務器啟動完成', level: 'info' },{ timestamp: new Date(Date.now() - 180000).toISOString(), message: '內存使用率過高', level: 'warning' },{ timestamp: new Date(Date.now() - 600000).toISOString(), message: '部署新應用成功', level: 'info' }],kafka: [{ timestamp: new Date().toISOString(), message: '新消息到達 topic: test', level: 'info' },{ timestamp: new Date(Date.now() - 240000).toISOString(), message: '消費者組 rebalance', level: 'warning' },{ timestamp: new Date(Date.now() - 500000).toISOString(), message: '創建新 topic: logs', level: 'info' }],redis: [{ timestamp: new Date().toISOString(), message: '內存使用量: 512MB', level: 'info' },{ timestamp: new Date(Date.now() - 300000).toISOString(), message: '連接數達到上限', level: 'warning' },{ timestamp: new Date(Date.now() - 600000).toISOString(), message: 'RDB 持久化完成', level: 'info' }]};const fetchLogs = async () => {if (selectedSources.length === 0) {setLogData([]);return;}setIsLoading(true);try {// 模擬API請求延遲await new Promise(resolve => setTimeout(resolve, 500));const allLogs = selectedSources.map(source => {const parser = getLogParser(source);return mockLogs[source].map(parser);});setLogData(allLogs.flat());} catch (err) {console.error('獲取日志失敗:', err);} finally {setIsLoading(false);}};fetchLogs();}, [hostName, selectedSources, apiEndpoint, timeInterval]);// 過濾日志useEffect(() => {let filtered = logData.filter(log => {const matchesSearch = log.message.toLowerCase().includes(searchTerm.toLowerCase());const matchesDate = (!dateRange[0] || log.timestamp >= dateRange[0]) &&(!dateRange[1] || log.timestamp <= dateRange[1]);return matchesSearch && matchesDate;});setFilteredData(filtered);}, [searchTerm, dateRange, logData]);return (<Card>{/* 主機名稱和時間范圍 */}<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '20px' }}><h3 style={{ margin: 0 }}>{hostName}</h3><div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}><div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}><inputtype="date"value={dateRange[0] ? dateRange[0].toISOString().split('T')[0] : ''}onChange={(e) => setDateRange([new Date(e.target.value), dateRange[1]])}disabled={isLoading}style={{padding: '8px',borderRadius: '8px',border: '1px solid #ddd',color: '#000',backgroundColor: '#fff',fontSize: '14px'}}/><span style={{ color: '#666' }}>至</span><inputtype="date"value={dateRange[1] ? dateRange[1].toISOString().split('T')[0] : ''}onChange={(e) => setDateRange([dateRange[0], new Date(e.target.value)])}disabled={isLoading}style={{padding: '8px',borderRadius: '8px',border: '1px solid #ddd',color: '#000',backgroundColor: '#fff',fontSize: '14px'}}/></div><select value={timeInterval} onChange={handleTimeIntervalChange}disabled={isLoading}style={{padding: '8px 16px',borderRadius: '8px',border: '1px solid #ddd',backgroundColor: '#fff',color: '#000',fontSize: '14px',cursor: 'pointer',appearance: 'none',backgroundImage: `url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e")`,backgroundRepeat: 'no-repeat',backgroundPosition: 'right 8px center',backgroundSize: '16px',minWidth: '120px'}}><option value="5m">最近5分鐘</option><option value="15m">最近15分鐘</option><option value="1h">最近1小時</option><option value="6h">最近6小時</option><option value="1d">最近1天</option><option value="1w">最近1周</option><option value="1mo">最近1個月</option></select></div></div>{/* 搜索日志 */}<div style={{ marginBottom: '20px' }}><inputtype="text"value={searchTerm}onChange={(e) => setSearchTerm(e.target.value)}placeholder="搜索日志..."disabled={isLoading}style={{ width: '100%', padding: '8px', borderRadius: '8px', border: '1px solid #ddd' }}/></div>{/* 選擇日志來源 */}<div style={{ marginBottom: '20px' }}><h4 style={{ marginBottom: '10px' }}>選擇日志來源</h4><div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>{middlewareOptions.map(option => (<buttonkey={option.value}style={{padding: '8px 16px',borderRadius: '8px',border: 'none',background: selectedSources.includes(option.value) ? '#007bff' : '#f0f0f0',color: selectedSources.includes(option.value) ? '#fff' : '#333',cursor: 'pointer',transition: 'all 0.2s ease'}}onClick={() => setSelectedSources(prev =>prev.includes(option.value)? prev.filter(item => item !== option.value): [...prev, option.value])}>{option.label}</button>))}</div></div>{/* 日志顯示區域 */}<div style={{ maxHeight: '400px', overflowY: 'auto' }}><table style={{ width: '100%', borderCollapse: 'collapse' }}><thead><tr style={{ background: '#f8f9fa' }}><th style={{ padding: '12px', textAlign: 'left' }}>時間</th><th style={{ padding: '12px', textAlign: 'left' }}>來源</th><th style={{ padding: '12px', textAlign: 'left' }}>級別</th><th style={{ padding: '12px', textAlign: 'left' }}>消息</th></tr></thead><tbody>{filteredData.map((log, index) => (<tr key={index} style={{ borderBottom: '1px solid #eee' }}><td style={{ padding: '12px' }}>{new Date(log.timestamp).toLocaleString()}</td><td style={{ padding: '12px' }}>{log.source}</td><td style={{ padding: '12px' }}>{log.level}</td><td style={{ padding: '12px' }}>{log.message}</td></tr>))}</tbody></table></div>{isLoading && <Loading>加載中...</Loading>}</Card>);
};// 主組件
const Knowledge = () => {const hosts = [{ name: '主機A' },{ name: '主機B' },{ name: '主機C' }];return (<div style={{ padding: '20px' }}>{hosts.map(host => (<LogModulekey={host.name}hostName={host.name}apiEndpoint="/api/logs"/>))}</div>);
};export default Knowledge;
5?? 報告導出功能
-
一鍵生成完整安全分析報告
-
導出格式:PDF / CSV
-
內容包括:設備狀態、漏洞信息、黑名單記錄、日志摘要等
-
import React from 'react';
import { PDFDownloadLink, Document, Page, Text, View, StyleSheet, Font } from '@react-pdf/renderer';// 注冊中文字體
Font.register({family: 'SimSun',src: '/fonts/SimSun.ttf', // 確保路徑正確
});// 定義PDF樣式
const styles = StyleSheet.create({page: {padding: 30,fontFamily: 'SimSun', // 使用中文字體},section: {marginBottom: 20,borderBottom: '1px solid #eee',paddingBottom: 10,},title: {fontSize: 24,marginBottom: 20,color: '#333',fontWeight: 'bold',},subtitle: {fontSize: 18,marginBottom: 15,color: '#555',},text: {fontSize: 14,marginBottom: 8,color: '#666',},table: {display: 'table',width: '100%',marginTop: 20,},tableRow: {display: 'table-row',},tableCell: {display: 'table-cell',padding: '8px 12px',border: '1px solid #ddd',},tableHeader: {backgroundColor: '#f5f5f5',fontWeight: 'bold',}
});// 主機信息組件
const HostInfo = ({ host, selected, onChange }) => {// 根據操作系統類型獲取圖標const getOSIcon = (os) => {const iconStyle = {fontSize: 48,color: '#666'};if (os.includes('Windows')) {return <span style={iconStyle}>🪟</span>; // Windows 圖標} else if (os.includes('Android')) {return <span style={iconStyle}>🤖</span>; // Android 圖標} else if (os.includes('macOS') || os.includes('iOS')) {return <span style={iconStyle}>🍎</span>; // 蘋果系統圖標} else if (os.includes('Linux') || os.includes('Ubuntu')) {return <span style={iconStyle}>🐧</span>; // Linux 圖標} else {return <span style={iconStyle}>💻</span>; // 默認圖標}};return (<div style={{ position: 'relative',padding: 20,backgroundColor: selected ? '#e6f7ff' : '#fafafa',borderRadius: 12,border: `1px solid ${selected ? '#40a9ff' : '#e8e8e8'}`,cursor: 'pointer',transition: 'all 0.3s ease',boxShadow: '0 2px 8px rgba(0,0,0,0.05)',':hover': {boxShadow: '0 4px 12px rgba(0,0,0,0.1)',transform: 'translateY(-2px)'}}} onClick={() => onChange()}>{/* 選擇按鈕 */}<div style={{position: 'absolute',top: 12,right: 12,width: 20,height: 20,borderRadius: '50%',border: `2px solid ${selected ? '#40a9ff' : '#d9d9d9'}`,backgroundColor: selected ? '#40a9ff' : '#fff',display: 'flex',alignItems: 'center',justifyContent: 'center',transition: 'all 0.3s ease'}}>{selected && (<span style={{ color: '#fff',fontSize: 12,lineHeight: 1}}>?</span>)}</div>{/* 系統圖標 */}<div style={{ textAlign: 'center',marginBottom: 16}}><div style={{ display: 'flex',justifyContent: 'center',marginBottom: 8}}>{getOSIcon(host.os)}</div><div style={{ fontSize: 14,color: '#666',fontWeight: 500}}>{host.os}</div></div>{/* 主機信息 */}<div style={{ borderTop: '1px solid #eee',paddingTop: 16}}><div style={{ display: 'flex',justifyContent: 'space-between',marginBottom: 8}}><div style={{ fontSize: 14, color: '#666' }}>主機名</div><div style={{ fontSize: 14, fontWeight: 500 }}>{host.name}</div></div><div style={{ display: 'flex',justifyContent: 'space-between',marginBottom: 8}}><div style={{ fontSize: 14, color: '#666' }}>IP地址</div><div style={{ fontSize: 14, fontWeight: 500 }}>{host.ip}</div></div><div style={{ display: 'flex',justifyContent: 'space-between',marginBottom: 8}}><div style={{ fontSize: 14, color: '##666' }}>狀態</div><div style={{ fontSize: 14,fontWeight: 500,color: host.status === '在線' ? '#52c41a' : '#f5222d'}}>{host.status}</div></div><div style={{ display: 'flex',justifyContent: 'space-between',marginBottom: 8}}><div style={{ fontSize: 14, color: '#666' }}>開放端口</div><div style={{ fontSize: 14, fontWeight: 500 }}>{host.openPorts.join(', ')}</div></div><div style={{ display: 'flex',justifyContent: 'space-between'}}><div style={{ fontSize: 14, color: '#666' }}>漏洞數量</div><div style={{ fontSize: 14,fontWeight: 500,color: host.vulnerabilities.length > 0 ? '#f5222d' : '#52c41a'}}>{host.vulnerabilities.length}</div></div></div></div>);
};// 漏洞分析卡片組件
const VulnerabilityCard = ({ host, vuln, selected, onChange }) => {return (<div style={{ position: 'relative',padding: 16,backgroundColor: selected ? '#e6f7ff' : vuln.severity === '高危' ? '#fff1f0' : '#fff7e6',borderRadius: 8,boxShadow: '0 2px 8px rgba(0,0,0,0.05)',cursor: 'pointer',transition: 'all 0.3s ease',':hover': {boxShadow: '0 4px 12px rgba(0,0,0,0.1)',transform: 'translateY(-2px)'}}} onClick={() => onChange()}>{/* 選擇按鈕 */}<div style={{position: 'absolute',top: 12,right: 12,width: 20,height: 20,borderRadius: '50%',border: `2px solid ${selected ? '#40a9ff' : '#d9d9d9'}`,backgroundColor: selected ? '#40a9ff' : '#fff',display: 'flex',alignItems: 'center',justifyContent: 'center',transition: 'all 0.3s ease'}}>{selected && (<span style={{ color: '#fff',fontSize: 12,lineHeight: 1}}>?</span>)}</div><div style={{ fontSize: 16, fontWeight: 500, marginBottom: 8 }}>{host.name} ({host.ip})</div><div style={{ color: '#666', marginBottom: 4 }}>漏洞類型: {vuln.type}</div><div style={{ color: '#666', marginBottom: 4 }}>風險等級: {vuln.severity}</div><div style={{ color: '#666', marginBottom: 4 }}>描述: {vuln.description || '暫無詳細描述'}</div><div style={{ color: '#666' }}>建議措施: {vuln.recommendation}</div></div>);
};// PDF內容組件
const MyDocument = ({ wifiSignal, vulnerabilities, hostCount, selectedHosts,selectedVulnerabilities,networkInfo,securityIssues
}) => {console.log('Selected Hosts:', selectedHosts);console.log('Selected Vulnerabilities:', selectedVulnerabilities);// PDF中的圖標映射const getOSIcon = (os) => {if (os.includes('Windows')) {return '🪟';} else if (os.includes('Android')) {return '🤖';} else if (os.includes('macOS') || os.includes('iOS')) {return '🍎';} else if (os.includes('Linux') || os.includes('Ubuntu')) {return '🐧';} else {return '💻';}};return (<Document><Page size="A4" style={styles.page}><View style={styles.section}><Text style={styles.title}>網絡安全測試報告</Text><Text style={styles.subtitle}>網絡概況</Text><Text style={styles.text}>? 當前WiFi信號強度: {wifiSignal}</Text><Text style={styles.text}>? 網絡類型: {networkInfo.type}</Text><Text style={styles.text}>? 局域網內部主機數量: {hostCount}</Text></View><View style={styles.section}><Text style={styles.subtitle}>主機信息</Text><View style={styles.table}><View style={[styles.tableRow, styles.tableHeader]}><Text style={styles.tableCell}>圖標</Text><Text style={styles.tableCell}>主機名</Text><Text style={styles.tableCell}>IP地址</Text><Text style={styles.tableCell}>狀態</Text><Text style={styles.tableCell}>操作系統</Text><Text style={styles.tableCell}>開放端口</Text><Text style={styles.tableCell}>漏洞數量</Text></View>{selectedHosts.map((host, index) => (<View key={index} style={styles.tableRow}><Text style={styles.tableCell}>{getOSIcon(host.os)}</Text><Text style={styles.tableCell}>{host.name}</Text><Text style={styles.tableCell}>{host.ip}</Text><Text style={styles.tableCell}>{host.status}</Text><Text style={styles.tableCell}>{host.os}</Text><Text style={styles.tableCell}>{host.openPorts.join(', ')}</Text><Text style={styles.tableCell}>{host.vulnerabilities.length}</Text></View>))}</View></View><View style={styles.section}><Text style={styles.subtitle}>漏洞分析</Text><View style={styles.table}><View style={[styles.tableRow, styles.tableHeader]}><Text style={styles.tableCell}>主機名</Text><Text style={styles.tableCell}>漏洞類型</Text><Text style={styles.tableCell}>風險等級</Text><Text style={styles.tableCell}>建議措施</Text></View>{selectedVulnerabilities.map((vuln, index) => (<View key={index} style={styles.tableRow}><Text style={styles.tableCell}>{vuln.host.name}</Text><Text style={styles.tableCell}>{vuln.type}</Text><Text style={styles.tableCell}>{vuln.severity}</Text><Text style={styles.tableCell}>{vuln.recommendation}</Text></View>))}</View></View></Page></Document>);
};// 主組件
const MakePDF = ({ wifiSignal = "強", vulnerabilities = 3, hostCount = 5 }) => {const [hosts, setHosts] = React.useState([{ name: 'PC-01', ip: '192.168.1.101', status: '在線', os: 'Windows 10',openPorts: [80, 443, 3389],vulnerabilities: [{ type: 'SMB漏洞', severity: '高危', recommendation: '更新系統補丁' },{ type: '弱密碼', severity: '中危', recommendation: '加強密碼策略' }],selected: false },{ name: 'PC-02', ip: '192.168.1.102', status: '離線', os: 'Ubuntu 20.04',openPorts: [22, 80],vulnerabilities: [{ type: 'SSH弱密碼', severity: '高危', recommendation: '使用密鑰認證' }],selected: false },{ name: 'PC-03', ip: '192.168.1.103', status: '在線', os: 'macOS 12',openPorts: [22, 5900],vulnerabilities: [{ type: 'VNC未加密', severity: '中危', recommendation: '啟用加密' }],selected: false },{ name: 'Phone-01', ip: '192.168.1.104', status: '在線', os: 'Android 12',openPorts: [8080],vulnerabilities: [{ type: '未加密通信', severity: '中危', recommendation: '啟用HTTPS' }],selected: false },{ name: 'Phone-02', ip: '192.168.1.105', status: '在線', os: 'iOS 15',openPorts: [443],vulnerabilities: [{ type: '越獄檢測', severity: '低危', recommendation: '檢查設備完整性' }],selected: false }]);const [selectedVulnerabilities, setSelectedVulnerabilities] = React.useState([]);const handleHostSelect = (index) => {const newHosts = [...hosts];newHosts[index].selected = !newHosts[index].selected;setHosts(newHosts);};const handleVulnerabilitySelect = (hostIndex, vulnIndex) => {const host = hosts[hostIndex];const vuln = host.vulnerabilities[vulnIndex];const isSelected = selectedVulnerabilities.some(v => v.host.name === host.name && v.type === vuln.type);if (isSelected) {setSelectedVulnerabilities(prev => prev.filter(v => !(v.host.name === host.name && v.type === vuln.type)));} else {setSelectedVulnerabilities(prev => [...prev, { ...vuln, host }]);}};const selectedHosts = hosts.filter(host => host.selected);const networkInfo = {type: '有線網絡',ip: '192.168.1.1',subnetMask: '255.255.255.0',gateway: '192.168.1.1'};const securityIssues = {openPorts: [80, 443, 22],threats: ['未加密的WiFi', '未更新的設備', '未配置的安全策略']};return (<div style={{ padding: 24,maxWidth: 1200,margin: '0 auto',backgroundColor: '#fff',borderRadius: 12,boxShadow: '0 4px 12px rgba(0,0,0,0.1)'}}><h2 style={{ color: '#333',marginBottom: 24,fontSize: 24,fontWeight: 600,textAlign: 'center'}}>網絡安全測試報告</h2><div style={{ marginBottom: 32 }}><h3 style={{ color: '#555',marginBottom: 16,fontSize: 20,fontWeight: 500}}>主機信息</h3><div style={{ display: 'grid',gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))',gap: 24}}>{hosts.map((host, index) => (<HostInfokey={index}host={host}selected={host.selected}onChange={() => handleHostSelect(index)}/>))}</div></div><div style={{ marginBottom: 32 }}><h3 style={{ color: '#555',marginBottom: 16,fontSize: 20}}>漏洞分析</h3><div style={{ display: 'grid',gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',gap: 16}}>{hosts.map((host, hostIndex) => (host.vulnerabilities.map((vuln, vulnIndex) => {const isSelected = selectedVulnerabilities.some(v => v.host.name === host.name && v.type === vuln.type);return (<VulnerabilityCardkey={`${hostIndex}-${vulnIndex}`}host={host}vuln={vuln}selected={isSelected}onChange={() => handleVulnerabilitySelect(hostIndex, vulnIndex)}/>);})))}</div></div><div style={{ textAlign: 'center',marginTop: 32,paddingTop: 24,borderTop: '1px solid #eee'}}><PDFDownloadLinkdocument={<MyDocumentwifiSignal={wifiSignal}vulnerabilities={vulnerabilities}hostCount={hostCount}selectedHosts={selectedHosts}selectedVulnerabilities={selectedVulnerabilities}networkInfo={networkInfo}securityIssues={securityIssues}/>}fileName="security_report.pdf">{({ loading }) => (<button style={{padding: '12px 32px',backgroundColor: '#1890ff',color: '#fff',border: 'none',borderRadius: 8,cursor: 'pointer',fontSize: 16,fontWeight: 500,transition: 'all 0.3s ease',':hover': {backgroundColor: '#40a9ff',transform: 'translateY(-2px)',boxShadow: '0 4px 12px rgba(24,144,255,0.3)'}}}>{loading ? '生成PDF中...' : '生成PDF'}</button>)}</PDFDownloadLink></div></div>);
};export default MakePDF;
六、使用方式簡要說明
👤 登錄流程
-
注冊+登錄驗證(用戶名/密碼)
-
權限控制區分不同用戶視圖
🔍 功能入口導航
-
儀表盤:首頁實時狀態概覽
-
數據包檢測:開始/停止抓包
-
漏洞掃描:點擊一鍵檢測
-
黑名單管理:IP添加/刪除
-
日志中心:按主機分類分析日志