Windows系统更新启用/永久禁用批处理工具
Windows系统更新启用/永久禁用批处理工具，复制代码,保存到到文件，后缀名改成bat,右键以管理员身份运行，根据提示选择即可 @echo off title 系统更新启用/禁用工具 :menu cls echo 请选择操作： echo 1. 禁用系统更新 echo 2. 启用系统更新 echo 3. 退出 set /p choice=请输入选项编号: if "%choice%"=="1" ( echo 正在禁用系统更新服务... net stop wuauserv sc config wuauserv start= disabled echo 正在禁用后台智能传输服务... net stop bits sc config bits start= disabled echo 正在禁用更新相关的计划任务... schtasks /Change /TN "\Microsoft\Windows\UpdateOrchestrator\ScheduledStart" /Disable >nul 2>&1 schtasks /Change /TN "\Microsoft\Windows\WindowsUpdate\Automatic App Update" /Disable >nul 2>&1 echo 系统更新已彻底禁用。 pause goto menu ) else if "%choice%"=="2" ( echo 正在启用系统更新服务... sc config wuauserv start= auto net start wuauserv echo 正在启用后台智能传输服务... sc config bits start= auto net start bits echo 正在启用更新相关的计划任务... schtasks /Change /TN "\Microsoft\Windows\UpdateOrchestrator\ScheduledStart" /Enable >nul 2>&1 schtasks /Change /TN "\Microsoft\Windows\WindowsUpdate\Automatic App Update" /Enable >nul 2>&1 echo 系统更新已启用。 pause goto menu ) else if "%choice%"=="3" ( exit ) else ( echo 输入无效，请输入有效的选项编号。 pause goto menu ) 如遇到右键以管理员身份运行出现乱码的问题，乱码的核心原因是文件编码与 CMD 终端的默认编码（通常为 ANSI/GBK）不匹配,按以下步骤操作: 用记事本打开 BAT 文件​ → 点击“文件” → “另存为”。 在“保存类型”下拉框中选择 ​​“所有文件”​，在“编码”下拉框中选择 ​​“ANSI”​​（中文系统下对应 GBK 编码） 覆盖原文件并重新以管理员身份运行脚本。
Linux系统history命令配置优化【加入记录IP地址和时间】
加入时间和执行IP地址【全局生效、针对所有用户】 vim /etc/profile # 在/etc/profile 末尾加入以下内容 if [ `who am i| awk '{print $NF}'|awk -F'(' '{print NF}'` -eq 2 ];then unset HISTTIMEFORMAT export HISTTIMEFORMAT="[%F %T]-[`whoami`]- at [`who am i| awk '{print $NF}'`] " else unset HISTTIMEFORMAT export HISTTIMEFORMAT="[%F %T]-[`whoami`]- at [`hostname`] " fi 使其生效 source /etc/profile history其他优化 不同的终端可以通过上翻可查询到命令 vim /etc/bashrc # 避免重复命令（包括非连续重复） export HISTCONTROL=ignoredups:erasedups # 退出时追加历史而非覆盖 shopt -s histappend # 实时同步历史命令（优化性能） export PROMPT_COMMAND='history -a; history -n; echo "$(date "+%F %T") $(whoami) $(tail -n1 ~/.bash_history)" >> /var/log/audit.log' 使其生效 source /etc/bashrc 调整大小，增加到10000条 vim /etc/bashrc export HISTSIZE=10000 使其生效 source /etc/bashrc
基础浏览器指纹检测 1. User-Agent 检测 // 检测浏览器基本信息 function detectUserAgent() { return { userAgent: navigator.userAgent, appName: navigator.appName, appVersion: navigator.appVersion, platform: navigator.platform, vendor: navigator.vendor, product: navigator.product }; } 2. 语言和地区检测 function detectLanguage() { return { language: navigator.language, languages: navigator.languages, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, locale: Intl.NumberFormat().resolvedOptions().locale }; } 3. 屏幕指纹检测 function detectScreen() { return { screenWidth: screen.width, screenHeight: screen.height, availWidth: screen.availWidth, availHeight: screen.availHeight, colorDepth: screen.colorDepth, pixelDepth: screen.pixelDepth, orientation: screen.orientation ? screen.orientation.type : 'unknown', devicePixelRatio: window.devicePixelRatio }; } Canvas 指纹检测 4. Canvas 2D 指纹 function getCanvas2DFingerprint() { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 设置文本样式 ctx.textBaseline = 'top'; ctx.font = '14px Arial'; ctx.fillStyle = '#f60'; ctx.fillRect(125, 1, 62, 20); // 绘制文本 ctx.fillStyle = '#069'; ctx.fillText('Canvas fingerprint', 2, 15); // 绘制更复杂的图形 ctx.globalCompositeOperation = 'multiply'; ctx.fillStyle = 'rgb(255,0,255)'; ctx.beginPath(); ctx.arc(50, 50, 50, 0, Math.PI * 2, true); ctx.closePath(); ctx.fill(); return canvas.toDataURL(); } 5. Canvas WebGL 指纹 function getWebGLFingerprint() { const canvas = document.createElement('canvas'); const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); if (!gl) return null; const fingerprint = { vendor: gl.getParameter(gl.VENDOR), renderer: gl.getParameter(gl.RENDERER), version: gl.getParameter(gl.VERSION), shadingLanguageVersion: gl.getParameter(gl.SHADING_LANGUAGE_VERSION), maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE), maxViewportDims: gl.getParameter(gl.MAX_VIEWPORT_DIMS), maxVertexAttribs: gl.getParameter(gl.MAX_VERTEX_ATTRIBS), maxVaryingVectors: gl.getParameter(gl.MAX_VARYING_VECTORS), aliasedLineWidthRange: gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE), aliasedPointSizeRange: gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE) }; // 获取扩展信息 const extensions = gl.getSupportedExtensions(); fingerprint.extensions = extensions; // 获取WebGL渲染指纹 const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, 'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}'); gl.compileShader(vertexShader); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, 'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}'); gl.compileShader(fragmentShader); const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program); program.attrVertex = gl.getAttribLocation(program, 'attrVertex'); program.uniformOffset = gl.getUniformLocation(program, 'uniformOffset'); const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,0,1,1,-1]), gl.STATIC_DRAW); gl.enableVertexAttribArray(program.attrVertex); gl.vertexAttribPointer(program.attrVertex, 2, gl.FLOAT, false, 0, 0); gl.uniform2f(program.uniformOffset, 1, 1); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3); // 读取像素数据作为指纹 const pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4); gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); fingerprint.renderFingerprint = Array.from(pixels).join(','); return fingerprint; } 音频指纹检测 6. AudioContext 指纹 function getAudioFingerprint() { return new Promise((resolve) => { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const oscillator = audioContext.createOscillator(); const analyser = audioContext.createAnalyser(); const gainNode = audioContext.createGain(); const scriptProcessor = audioContext.createScriptProcessor(4096, 1, 1); oscillator.type = 'triangle'; oscillator.frequency.setValueAtTime(10000, audioContext.currentTime); gainNode.gain.setValueAtTime(0, audioContext.currentTime); oscillator.connect(analyser); analyser.connect(scriptProcessor); scriptProcessor.connect(gainNode); gainNode.connect(audioContext.destination); scriptProcessor.onaudioprocess = function(bins) { bins.outputBuffer.getChannelData(0).set(bins.inputBuffer.getChannelData(0)); const data = bins.inputBuffer.getChannelData(0); let sum = 0; for (let i = 0; i < data.length; i++) { sum += Math.abs(data[i]); } oscillator.disconnect(); scriptProcessor.disconnect(); analyser.disconnect(); gainNode.disconnect(); resolve(sum.toString()); }; oscillator.start(0); }); } 字体指纹检测 7. 字体检测 function detectFonts() { const baseFonts = ['monospace', 'sans-serif', 'serif']; const testFonts = [ 'Arial', 'Arial Black', 'Arial Narrow', 'Arial Rounded MT Bold', 'Calibri', 'Cambria', 'Candara', 'Comic Sans MS', 'Consolas', 'Courier', 'Courier New', 'Geneva', 'Georgia', 'Helvetica', 'Impact', 'Lucida Console', 'Lucida Sans Unicode', 'Microsoft Sans Serif', 'Monaco', 'Palatino', 'Tahoma', 'Times', 'Times New Roman', 'Trebuchet MS', 'Verdana', '宋体', '微软雅黑', '黑体' ]; const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const testString = 'mmmmmmmmmmlli'; const testSize = '72px'; const baselines = {}; baseFonts.forEach(font => { ctx.font = `${testSize} ${font}`; baselines[font] = ctx.measureText(testString).width; }); const detectedFonts = []; testFonts.forEach(font => { let detected = false; baseFonts.forEach(baseFont => { ctx.font = `${testSize} ${font}, ${baseFont}`; const width = ctx.measureText(testString).width; if (width !== baselines[baseFont]) { detected = true; } }); if (detected) { detectedFonts.push(font); } }); return detectedFonts; } 硬件指纹检测 8. 硬件信息检测 function detectHardware() { const fingerprint = { hardwareConcurrency: navigator.hardwareConcurrency, deviceMemory: navigator.deviceMemory, maxTouchPoints: navigator.maxTouchPoints, connection: null }; // 网络连接信息 if (navigator.connection) { fingerprint.connection = { effectiveType: navigator.connection.effectiveType, downlink: navigator.connection.downlink, rtt: navigator.connection.rtt, saveData: navigator.connection.saveData }; } return fingerprint; } 9. 电池信息检测 function detectBattery() { if ('getBattery' in navigator) { return navigator.getBattery().then(battery => { return { charging: battery.charging, chargingTime: battery.chargingTime, dischargingTime: battery.dischargingTime, level: battery.level }; }); } return Promise.resolve(null); } 时间相关指纹 10. 时间精度检测 function detectTiming() { const start = performance.now(); for (let i = 0; i < 1000; i++) { Math.random(); } const end = performance.now(); return { performanceNowResolution: measureTimeResolution(() => performance.now()), dateNowResolution: measureTimeResolution(() => Date.now()), executionTime: end - start, timezoneOffset: new Date().getTimezoneOffset(), timezone: Intl.DateTimeFormat().resolvedOptions().timeZone }; } function measureTimeResolution(timeFunction) { const times = []; for (let i = 0; i < 1000; i++) { times.push(timeFunction()); } const unique = [...new Set(times)]; return { samples: times.length, uniqueValues: unique.length, resolution: unique.length / times.length }; } WebRTC 指纹检测 11. WebRTC 本地IP检测 function detectWebRTCIPs() { return new Promise((resolve) => { const ips = []; const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }); pc.createDataChannel(''); pc.createOffer().then(offer => pc.setLocalDescription(offer)); pc.onicecandidate = (ice) => { if (!ice || !ice.candidate || !ice.candidate.candidate) return; const candidate = ice.candidate.candidate; const ip = candidate.split(' ')[4]; if (ip && ips.indexOf(ip) === -1) { ips.push(ip); } }; setTimeout(() => { pc.close(); resolve(ips); }, 2000); }); } 行为指纹检测 12. 鼠标行为检测 function detectMouseBehavior() { const mouseData = { movements: [], clicks: [], startTime: Date.now() }; document.addEventListener('mousemove', (e) => { mouseData.movements.push({ x: e.clientX, y: e.clientY, timestamp: Date.now() - mouseData.startTime }); }); document.addEventListener('click', (e) => { mouseData.clicks.push({ x: e.clientX, y: e.clientY, timestamp: Date.now() - mouseData.startTime, button: e.button }); }); return mouseData; } 13. 键盘行为检测 function detectKeyboardBehavior() { const keyData = { keystrokes: [], startTime: Date.now() }; document.addEventListener('keydown', (e) => { keyData.keystrokes.push({ key: e.key, code: e.code, timestamp: Date.now() - keyData.startTime, type: 'down' }); }); document.addEventListener('keyup', (e) => { keyData.keystrokes.push({ key: e.key, code: e.code, timestamp: Date.now() - keyData.startTime, type: 'up' }); }); return keyData; } 插件和扩展检测 14. 浏览器插件检测 function detectPlugins() { const plugins = []; for (let i = 0; i < navigator.plugins.length; i++) { const plugin = navigator.plugins[i]; plugins.push({ name: plugin.name, description: plugin.description, filename: plugin.filename, version: plugin.version }); } return plugins; } 15. Flash 检测 function detectFlash() { let hasFlash = false; let flashVersion = 'unknown'; if (navigator.plugins && navigator.plugins.length > 0) { for (let i = 0; i < navigator.plugins.length; i++) { if (navigator.plugins[i].name.toLowerCase().includes('flash')) { hasFlash = true; flashVersion = navigator.plugins[i].version || navigator.plugins[i].description; break; } } } return { hasFlash, version: flashVersion }; } 自动化检测 16. Selenium/WebDriver 检测 function detectAutomation() { const checks = { webdriver: navigator.webdriver !== undefined, chrome: window.chrome === undefined, permissions: navigator.permissions === undefined, plugins: navigator.plugins.length === 0, languages: navigator.languages.length === 0, webgl: (() => { const canvas = document.createElement('canvas'); const gl = canvas.getContext('webgl'); if (!gl) return false; const debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); return debugInfo && gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL).includes('SwiftShader'); })(), userAgent: /HeadlessChrome/.test(navigator.userAgent), viewport: window.outerHeight === 0, documentReady: document.readyState !== 'complete' }; return checks; } 17. Puppeteer 检测 function detectPuppeteer() { const checks = { runtimeEnabled: !!window.__chrome_app_runtime__, permissionsQuery: (() => { try { navigator.permissions.query({name: 'notifications'}); return false; } catch (e) { return true; } })(), chromeNotifications: !window.Notification, chromeRuntime: !!window.chrome && !!window.chrome.runtime, frameElement: window.top !== window.self, headlessUserAgent: /HeadlessChrome/.test(navigator.userAgent) }; return checks; } 综合指纹生成 18. 生成综合指纹 async function generateFingerprint() { const fingerprint = { basic: detectUserAgent(), screen: detectScreen(), language: detectLanguage(), hardware: detectHardware(), canvas2d: getCanvas2DFingerprint(), webgl: getWebGLFingerprint(), fonts: detectFonts(), plugins: detectPlugins(), timing: detectTiming(), automation: detectAutomation(), puppeteer: detectPuppeteer(), timestamp: Date.now() }; // 异步获取的指纹 try { fingerprint.audio = await getAudioFingerprint(); fingerprint.battery = await detectBattery(); fingerprint.webrtcIPs = await detectWebRTCIPs(); } catch (e) { console.log('异步指纹获取失败:', e); } // 生成指纹哈希 const fingerprintString = JSON.stringify(fingerprint); fingerprint.hash = await hashFingerprint(fingerprintString); return fingerprint; } async function hashFingerprint(data) { const encoder = new TextEncoder(); const dataBuffer = encoder.encode(data); const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); } 使用示例 19. 完整检测脚本 // 页面加载完成后执行指纹检测 document.addEventListener('DOMContentLoaded', async () => { console.log('开始指纹检测...'); const fingerprint = await generateFingerprint(); console.log('指纹检测完成:', fingerprint); console.log('指纹哈希:', fingerprint.hash); // 发送指纹到服务器 // fetch('/api/fingerprint', { // method: 'POST', // headers: { 'Content-Type': 'application/json' }, // body: JSON.stringify(fingerprint) // }); }); 常见的指纹检测绕过的方法 基础浏览器指纹绕过 1. User-Agent 伪装 // 方法1: Object.defineProperty 重定义 Object.defineProperty(navigator, 'userAgent', { value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', writable: false, enumerable: true, configurable: false }); // 方法2: 完整浏览器信息伪装 const fakeNavigator = { userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', appName: 'Netscape', appVersion: '5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', platform: 'Win32', vendor: 'Google Inc.', product: 'Gecko', language: 'en-US', languages: ['en-US', 'en'], onLine: true, cookieEnabled: true, hardwareConcurrency: 8, maxTouchPoints: 0, deviceMemory: 8 }; Object.keys(fakeNavigator).forEach(key => { Object.defineProperty(navigator, key, { value: fakeNavigator[key], writable: false, enumerable: true, configurable: false }); }); 2. 屏幕信息标准化 // 标准化屏幕分辨率 const fakeScreen = { width: 1920, height: 1080, availWidth: 1920, availHeight: 1040, colorDepth: 24, pixelDepth: 24, orientation: { angle: 0, type: 'landscape-primary' } }; Object.keys(fakeScreen).forEach(key => { if (key === 'orientation') { Object.defineProperty(screen, key, { value: fakeScreen[key], writable: false }); } else { Object.defineProperty(screen, key, { value: fakeScreen[key], writable: false, enumerable: true, configurable: false }); } }); // 设备像素比标准化 Object.defineProperty(window, 'devicePixelRatio', { value: 1, writable: false }); 3. 时区和语言统一 // 重写时区检测 const originalDateTimeFormat = Intl.DateTimeFormat; Intl.DateTimeFormat = function(...args) { const instance = new originalDateTimeFormat(...args); const originalResolvedOptions = instance.resolvedOptions; instance.resolvedOptions = function() { const options = originalResolvedOptions.call(this); options.timeZone = 'America/New_York'; options.locale = 'en-US'; return options; }; return instance; }; // 重写时区偏移 const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset; Date.prototype.getTimezoneOffset = function() { return 300; // UTC-5 (Eastern Time) }; Canvas 指纹绕过 4. Canvas 2D 指纹干扰 // 方法1: 添加随机噪声 const originalToDataURL = HTMLCanvasElement.prototype.toDataURL; HTMLCanvasElement.prototype.toDataURL = function(type, quality) { if (!this._fingerprinted) { const ctx = this.getContext('2d'); const imageData = ctx.getImageData(0, 0, this.width, this.height); // 添加轻微的随机噪声 for (let i = 0; i < imageData.data.length; i += 4) { if (Math.random() < 0.001) { const noise = Math.floor(Math.random() * 5) - 2; imageData.data[i] = Math.max(0, Math.min(255, imageData.data[i] + noise)); imageData.data[i + 1] = Math.max(0, Math.min(255, imageData.data[i + 1] + noise)); imageData.data[i + 2] = Math.max(0, Math.min(255, imageData.data[i + 2] + noise)); } } ctx.putImageData(imageData, 0, 0); this._fingerprinted = true; } return originalToDataURL.apply(this, arguments); }; // 方法2: 返回固定的Canvas指纹 HTMLCanvasElement.prototype.toDataURL = function() { return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; }; // 方法3: Hook getImageData const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData; CanvasRenderingContext2D.prototype.getImageData = function(sx, sy, sw, sh) { const imageData = originalGetImageData.apply(this, arguments); // 添加轻微扰动 for (let i = 0; i < imageData.data.length; i += 4) { if (Math.random() < 0.001) { imageData.data[i] = (imageData.data[i] + Math.floor(Math.random() * 3) - 1) % 256; } } return imageData; }; 5. WebGL 指纹伪装 // Hook WebGL getParameter const originalGetParameter = WebGLRenderingContext.prototype.getParameter; WebGLRenderingContext.prototype.getParameter = function(parameter) { // 常见的WebGL参数伪装 const fakeValues = { [this.VENDOR]: 'Intel Inc.', [this.RENDERER]: 'Intel(R) HD Graphics 4000', [this.VERSION]: 'WebGL 1.0 (OpenGL ES 2.0 Chromium)', [this.SHADING_LANGUAGE_VERSION]: 'WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)', [this.MAX_TEXTURE_SIZE]: 16384, [this.MAX_VIEWPORT_DIMS]: new Int32Array([16384, 16384]), [this.MAX_VERTEX_ATTRIBS]: 16, [this.MAX_VARYING_VECTORS]: 15, [this.ALIASED_LINE_WIDTH_RANGE]: new Float32Array([1, 1]), [this.ALIASED_POINT_SIZE_RANGE]: new Float32Array([1, 1024]) }; if (parameter in fakeValues) { return fakeValues[parameter]; } return originalGetParameter.apply(this, arguments); }; // Hook WebGL扩展 const originalGetExtension = WebGLRenderingContext.prototype.getExtension; WebGLRenderingContext.prototype.getExtension = function(name) { // 返回标准化的扩展列表 const standardExtensions = [ 'ANGLE_instanced_arrays', 'EXT_blend_minmax', 'EXT_color_buffer_half_float', 'EXT_disjoint_timer_query', 'EXT_frag_depth', 'EXT_shader_texture_lod', 'EXT_texture_filter_anisotropic', 'WEBKIT_EXT_texture_filter_anisotropic', 'EXT_sRGB', 'OES_element_index_uint', 'OES_standard_derivatives', 'OES_texture_float', 'OES_texture_float_linear', 'OES_texture_half_float', 'OES_texture_half_float_linear', 'OES_vertex_array_object', 'WEBGL_color_buffer_float', 'WEBGL_compressed_texture_s3tc', 'WEBKIT_WEBGL_compressed_texture_s3tc', 'WEBGL_debug_renderer_info', 'WEBGL_debug_shaders', 'WEBGL_depth_texture', 'WEBKIT_WEBGL_depth_texture', 'WEBGL_draw_buffers', 'WEBGL_lose_context', 'WEBKIT_WEBGL_lose_context' ]; if (standardExtensions.includes(name)) { return originalGetExtension.apply(this, arguments); } return null; }; // Hook getSupportedExtensions const originalGetSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions; WebGLRenderingContext.prototype.getSupportedExtensions = function() { return [ 'ANGLE_instanced_arrays', 'EXT_blend_minmax', 'EXT_color_buffer_half_float', 'EXT_disjoint_timer_query', 'EXT_frag_depth', 'EXT_shader_texture_lod', 'EXT_texture_filter_anisotropic', 'WEBKIT_EXT_texture_filter_anisotropic', 'EXT_sRGB', 'OES_element_index_uint', 'OES_standard_derivatives', 'OES_texture_float', 'OES_texture_float_linear', 'OES_texture_half_float', 'OES_texture_half_float_linear', 'OES_vertex_array_object', 'WEBGL_color_buffer_float', 'WEBGL_compressed_texture_s3tc', 'WEBKIT_WEBGL_compressed_texture_s3tc', 'WEBGL_debug_renderer_info', 'WEBGL_debug_shaders', 'WEBGL_depth_texture', 'WEBKIT_WEBGL_depth_texture', 'WEBGL_draw_buffers', 'WEBGL_lose_context', 'WEBKIT_WEBGL_lose_context' ]; }; 音频指纹绕过 6. AudioContext 指纹干扰 // 方法1: 添加随机噪声到音频输出 const originalCreateOscillator = AudioContext.prototype.createOscillator; AudioContext.prototype.createOscillator = function() { const oscillator = originalCreateOscillator.apply(this, arguments); const originalStart = oscillator.start; oscillator.start = function() { // 添加微小的频率偏移 const offset = (Math.random() - 0.5) * 0.001; this.frequency.value += offset; return originalStart.apply(this, arguments); }; return oscillator; }; // 方法2: Hook AnalyserNode const originalCreateAnalyser = AudioContext.prototype.createAnalyser; AudioContext.prototype.createAnalyser = function() { const analyser = originalCreateAnalyser.apply(this, arguments); const originalGetFloatFrequencyData = analyser.getFloatFrequencyData; analyser.getFloatFrequencyData = function(array) { originalGetFloatFrequencyData.apply(this, arguments); // 添加轻微噪声 for (let i = 0; i < array.length; i++) { array[i] += (Math.random() - 0.5) * 0.001; } }; return analyser; }; // 方法3: 完全禁用AudioContext if (typeof AudioContext !== 'undefined') { AudioContext = function() { throw new Error('AudioContext is not supported'); }; } if (typeof webkitAudioContext !== 'undefined') { webkitAudioContext = function() { throw new Error('webkitAudioContext is not supported'); }; } 字体指纹绕过 7. 字体检测绕过 // 方法1: Hook measureText返回固定宽度 const originalMeasureText = CanvasRenderingContext2D.prototype.measureText; CanvasRenderingContext2D.prototype.measureText = function(text) { const result = originalMeasureText.apply(this, arguments); // 为常见字体返回标准化的宽度 const fontFamilies = ['Arial', 'Helvetica', 'Times', 'Courier', 'Verdana', 'Georgia', 'Palatino', 'Garamond', 'Bookman', 'Comic Sans MS', 'Trebuchet MS', 'Arial Black', 'Impact']; for (const font of fontFamilies) { if (this.font.includes(font)) { // 返回固定宽度，消除字体差异 Object.defineProperty(result, 'width', { value: text.length * 12, // 简单的固定宽度计算 writable: false }); break; } } return result; }; // 方法2: 标准化offsetWidth和offsetHeight const originalOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth'); const originalOffsetHeight = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight'); Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { get: function() { if (this.style.fontFamily) { return 100; // 返回固定宽度 } return originalOffsetWidth.get.call(this); }, configurable: true }); Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { get: function() { if (this.style.fontFamily) { return 20; // 返回固定高度 } return originalOffsetHeight.get.call(this); }, configurable: true }); 时间指纹绕过 8. 性能时间戳干扰 // performance.now() 干扰 const originalPerformanceNow = performance.now; let performanceStartTime = originalPerformanceNow.call(performance); performance.now = function() { const realTime = originalPerformanceNow.call(performance); const fakeTime = realTime - performanceStartTime; // 添加随机偏移，但保持单调递增 const offset = Math.random() * 0.1; return Math.floor(fakeTime + offset); }; // Date.now() 标准化 const originalDateNow = Date.now; Date.now = function() { const realTime = originalDateNow(); // 将时间戳模糊化到最近的秒 return Math.floor(realTime / 1000) * 1000; }; // 高精度时间戳保护 if (typeof PerformanceObserver !== 'undefined') { PerformanceObserver = function() { throw new Error('PerformanceObserver is not supported'); }; } 硬件指纹绕过 9. CPU 和内存信息伪装 // 硬件并发数伪装 Object.defineProperty(navigator, 'hardwareConcurrency', { value: 8, writable: false, enumerable: true, configurable: false }); // 设备内存伪装 Object.defineProperty(navigator, 'deviceMemory', { value: 8, writable: false, enumerable: true, configurable: false }); // 触摸点数伪装 Object.defineProperty(navigator, 'maxTouchPoints', { value: 0, writable: false, enumerable: true, configurable: false }); // 网络连接信息伪装 if (navigator.connection) { Object.defineProperty(navigator, 'connection', { value: { effectiveType: '4g', downlink: 10, rtt: 100, saveData: false, addEventListener: function() {}, removeEventListener: function() {} }, writable: false, enumerable: true, configurable: false }); } 10. 电池信息隐藏 // 禁用电池API if ('getBattery' in navigator) { navigator.getBattery = function() { return Promise.reject(new Error('Battery API is not supported')); }; } // 隐藏电池事件 if ('battery' in navigator) { delete navigator.battery; } WebRTC 指纹绕过 11. 本地IP地址隐藏 // 方法1: 禁用WebRTC const originalRTCPeerConnection = window.RTCPeerConnection; window.RTCPeerConnection = function() { throw new Error('WebRTC is disabled'); }; if (window.webkitRTCPeerConnection) { window.webkitRTCPeerConnection = function() { throw new Error('WebRTC is disabled'); }; } if (window.mozRTCPeerConnection) { window.mozRTCPeerConnection = function() { throw new Error('WebRTC is disabled'); }; } // 方法2: 过滤ICE候选 const originalCreateOffer = RTCPeerConnection.prototype.createOffer; RTCPeerConnection.prototype.createOffer = function() { const offer = originalCreateOffer.apply(this, arguments); const originalOnIceCandidate = this.onicecandidate; this.onicecandidate = function(event) { if (event.candidate && event.candidate.candidate) { // 过滤本地IP地址 const candidate = event.candidate.candidate; if (candidate.includes('192.168.') || candidate.includes('10.') || candidate.includes('172.')) { return; // 阻止本地IP泄露 } } if (originalOnIceCandidate) { originalOnIceCandidate.call(this, event); } }; return offer; }; 插件和扩展绕过 12. 浏览器插件伪装 // 伪装标准插件列表 const fakePlugins = [ { name: 'Chrome PDF Plugin', description: 'Portable Document Format', filename: 'internal-pdf-viewer', version: '1.0.0.0' }, { name: 'Chrome PDF Viewer', description: '', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', version: '1.0.0.0' }, { name: 'Native Client', description: '', filename: 'internal-nacl-plugin', version: '1.0.0.0' } ]; // 重写plugins属性 Object.defineProperty(navigator, 'plugins', { value: { length: fakePlugins.length, ...fakePlugins, item: function(index) { return this[index] || null; }, namedItem: function(name) { return Array.from(this).find(plugin => plugin.name === name) || null; }, refresh: function() {} }, writable: false, enumerable: true, configurable: false }); // 重写mimeTypes Object.defineProperty(navigator, 'mimeTypes', { value: { length: 0, item: function() { return null; }, namedItem: function() { return null; } }, writable: false, enumerable: true, configurable: false }); 自动化检测绕过 13. Selenium/WebDriver 检测绕过 // 删除webdriver属性 Object.defineProperty(navigator, 'webdriver', { get: () => undefined, enumerable: true, configurable: true }); // 删除webdriver属性的另一种方法 delete navigator.__proto__.webdriver; // 伪装Chrome对象 if (!window.chrome) { window.chrome = { runtime: { onConnect: null, onMessage: null }, loadTimes: function() { return { commitLoadTime: Date.now() / 1000 - Math.random(), finishDocumentLoadTime: Date.now() / 1000 - Math.random(), finishLoadTime: Date.now() / 1000 - Math.random(), firstPaintAfterLoadTime: Date.now() / 1000 - Math.random(), firstPaintTime: Date.now() / 1000 - Math.random(), navigationType: 'Other', npnNegotiatedProtocol: 'h2', requestTime: Date.now() / 1000 - Math.random(), startLoadTime: Date.now() / 1000 - Math.random(), wasAlternateProtocolAvailable: false, wasFetchedViaSpdy: true, wasNpnNegotiated: true }; }, csi: function() { return { startE: Date.now(), onloadT: Date.now(), pageT: Math.random() * 1000, tran: 15 }; } }; } // 伪装Notification权限 const originalQuery = Permissions.prototype.query; Permissions.prototype.query = function(parameters) { if (parameters.name === 'notifications') { return Promise.resolve({ state: 'granted', onchange: null }); } return originalQuery.apply(this, arguments); }; // 修复outerHeight和outerWidth if (window.outerHeight === 0) { Object.defineProperty(window, 'outerHeight', { value: window.innerHeight, writable: false }); } if (window.outerWidth === 0) { Object.defineProperty(window, 'outerWidth', { value: window.innerWidth, writable: false }); } 14. Puppeteer 检测绕过 // 删除HeadlessChrome标识 if (navigator.userAgent.includes('HeadlessChrome')) { Object.defineProperty(navigator, 'userAgent', { value: navigator.userAgent.replace('HeadlessChrome', 'Chrome'), writable: false }); } // 伪装iframe检测 Object.defineProperty(window, 'frameElement', { value: null, writable: false }); // 伪装document.hidden Object.defineProperty(document, 'hidden', { value: false, writable: false }); // 伪装visibilityState Object.defineProperty(document, 'visibilityState', { value: 'visible', writable: false }); // 修复console.debug if (!console.debug) { console.debug = console.log; } Node.js环境补环境 15. 浏览器环境完整模拟 // 隐藏Node.js特征 const nodeFeatures = [ 'process', 'global', 'Buffer', 'require', 'module', 'exports', '__dirname', '__filename', 'setImmediate', 'clearImmediate' ]; nodeFeatures.forEach(feature => { try { delete global[feature]; } catch (e) { // ignore } }); // 创建window对象 global.window = global; global.self = global; global.top = global; global.parent = global; // 添加基础浏览器对象 global.document = { createElement: function(tag) { const element = { tagName: tag.toUpperCase(), style: {}, setAttribute: function() {}, getAttribute: function() { return ''; }, appendChild: function() {}, removeChild: function() {}, addEventListener: function() {}, removeEventListener: function() {}, click: function() {}, focus: function() {}, blur: function() {} }; if (tag === 'canvas') { element.getContext = function(type) { if (type === '2d') { return { fillText: function() {}, measureText: function(text) { return { width: text.length * 8 }; }, fillRect: function() {}, getImageData: function() { return { data: new Uint8ClampedArray(400) }; }, putImageData: function() {}, clearRect: function() {}, beginPath: function() {}, arc: function() {}, fill: function() {}, closePath: function() {} }; } return null; }; element.toDataURL = function() { return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; }; } return element; }, getElementById: function() { return this.createElement('div'); }, getElementsByTagName: function() { return []; }, querySelector: function() { return this.createElement('div'); }, querySelectorAll: function() { return []; }, body: {}, head: {}, readyState: 'complete', cookie: '', domain: 'localhost', URL: 'http://localhost' }; // 添加navigator对象 global.navigator = { userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', appName: 'Netscape', appVersion: '5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', platform: 'Win32', vendor: 'Google Inc.', language: 'en-US', languages: ['en-US', 'en'], onLine: true, cookieEnabled: true, hardwareConcurrency: 8, maxTouchPoints: 0, deviceMemory: 8, plugins: { length: 0 }, mimeTypes: { length: 0 } }; // 添加screen对象 global.screen = { width: 1920, height: 1080, availWidth: 1920, availHeight: 1040, colorDepth: 24, pixelDepth: 24 }; // 添加location对象 global.location = { href: 'http://localhost', origin: 'http://localhost', protocol: 'http:', hostname: 'localhost', port: '', pathname: '/', search: '', hash: '' }; // 添加history对象 global.history = { length: 1, pushState: function() {}, replaceState: function() {}, go: function() {}, back: function() {}, forward: function() {} }; // 添加常用函数 global.setTimeout = setTimeout; global.setInterval = setInterval; global.clearTimeout = clearTimeout; global.clearInterval = clearInterval; global.btoa = function(str) { return Buffer.from(str, 'binary').toString('base64'); }; global.atob = function(str) { return Buffer.from(str, 'base64').toString('binary'); }; // 添加XMLHttpRequest global.XMLHttpRequest = class { constructor() { this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.responseXML = null; this.onreadystatechange = null; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; this.readyState = 1; } setRequestHeader(name, value) { this.headers = this.headers || {}; this.headers[name] = value; } send(data) { this.readyState = 4; this.status = 200; this.statusText = 'OK'; this.responseText = '{}'; if (this.onreadystatechange) { this.onreadystatechange(); } } abort() { this.readyState = 0; } }; // 添加URL构造函数 global.URL = class { constructor(url, base) { const urlObj = new (require('url').URL)(url, base); Object.assign(this, urlObj); } }; // 添加FormData global.FormData = class { constructor() { this.data = new Map(); } append(name, value) { this.data.set(name, value); } get(name) { return this.data.get(name); } has(name) { return this.data.has(name); } delete(name) { this.data.delete(name); } }; 综合防检测脚本 16. 一键防检测脚本 (function() { 'use strict'; console.log('启动指纹防护...'); // 1. 基础信息伪装 const fakeUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; Object.defineProperty(navigator, 'userAgent', { value: fakeUserAgent, writable: false }); Object.defineProperty(navigator, 'platform', { value: 'Win32', writable: false }); Object.defineProperty(navigator, 'language', { value: 'en-US', writable: false }); Object.defineProperty(navigator, 'languages', { value: ['en-US', 'en'], writable: false }); // 2. 硬件信息标准化 Object.defineProperty(navigator, 'hardwareConcurrency', { value: 8, writable: false }); Object.defineProperty(navigator, 'deviceMemory', { value: 8, writable: false }); // 3. 屏幕信息标准化 Object.defineProperties(screen, { width: { value: 1920, writable: false }, height: { value: 1080, writable: false }, availWidth: { value: 1920, writable: false }, availHeight: { value: 1040, writable: false }, colorDepth: { value: 24, writable: false } }); // 4. Canvas指纹干扰 const originalToDataURL = HTMLCanvasElement.prototype.toDataURL; HTMLCanvasElement.prototype.toDataURL = function() { const canvas = this; const ctx = canvas.getContext('2d'); // 添加轻微噪声 if (!this._protected) { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); for (let i = 0; i < imageData.data.length; i += 4) { if (Math.random() < 0.001) { imageData.data[i] = (imageData.data[i] + Math.floor(Math.random() * 5) - 2) % 256; } } ctx.putImageData(imageData, 0, 0); this._protected = true; } return originalToDataURL.apply(this, arguments); }; // 5. WebGL指纹标准化 const originalGetParameter = WebGLRenderingContext.prototype.getParameter; WebGLRenderingContext.prototype.getParameter = function(parameter) { const fakeValues = { [this.VENDOR]: 'Intel Inc.', [this.RENDERER]: 'Intel(R) HD Graphics 4000', [this.VERSION]: 'WebGL 1.0 (OpenGL ES 2.0 Chromium)', [this.SHADING_LANGUAGE_VERSION]: 'WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)' }; return fakeValues[parameter] || originalGetParameter.apply(this, arguments); }; // 6. 时间API干扰 const originalPerformanceNow = performance.now; performance.now = function() { return Math.floor(originalPerformanceNow.apply(this, arguments)); }; // 7. 字体检测绕过 const originalMeasureText = CanvasRenderingContext2D.prototype.measureText; CanvasRenderingContext2D.prototype.measureText = function(text) { const result = originalMeasureText.apply(this, arguments); // 标准化测量结果 if (this.font.includes('Arial') || this.font.includes('Helvetica')) { Object.defineProperty(result, 'width', { value: Math.floor(result.width), writable: false }); } return result; }; // 8. 自动化检测绕过 if (navigator.webdriver) { Object.defineProperty(navigator, 'webdriver', { value: false, writable: false }); } // 9. Puppeteer检测绕过 if (navigator.userAgent.includes('HeadlessChrome')) { Object.defineProperty(navigator, 'userAgent', { value: navigator.userAgent.replace('HeadlessChrome', 'Chrome'), writable: false }); } // 10. 插件标准化 Object.defineProperty(navigator, 'plugins', { value: { length: 3, 0: { name: 'Chrome PDF Plugin', description: 'Portable Document Format' }, 1: { name: 'Chrome PDF Viewer', description: '' }, 2: { name: 'Native Client', description: '' } }, writable: false }); console.log('指纹防护已启用'); })(); 17. Tampermonkey 用户脚本 // ==UserScript== // @name 反指纹检测 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 防止网站指纹识别 // @AuThor You // @match *://*/* // @grant none // @run-at document-start // ==/UserScript== (function() { 'use strict'; // 在页面加载前执行防护代码 const script = document.createElement('script'); script.textContent = ` // 这里放入上面的综合防检测脚本代码 ${综合防检测脚本} `; (document.head || document.documentElement).appendChild(script); script.remove(); })(); 高级绕过技术 18. 动态指纹生成 // 生成随机但一致的指纹 class FingerprintSpoofing { constructor() { this.seed = this.generateSeed(); this.rng = this.seededRandom(this.seed); } generateSeed() { // 基于域名生成固定种子，确保同一网站指纹一致 const domain = window.location.hostname; let hash = 0; for (let i = 0; i < domain.length; i++) { const char = domain.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // 转换为32位整数 } return Math.abs(hash); } seededRandom(seed) { return function() { seed = (seed * 9301 + 49297) % 233280; return seed / 233280; }; } getRandomUserAgent() { const userAgents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' ]; const index = Math.floor(this.rng() * userAgents.length); return userAgents[index]; } getRandomScreenResolution() { const resolutions = [ [1920, 1080], [1366, 768], [1440, 900], [1536, 864], [1280, 720] ]; const index = Math.floor(this.rng() * resolutions.length); return resolutions[index]; } apply() { // 应用随机指纹 const userAgent = this.getRandomUserAgent(); const [width, height] = this.getRandomScreenResolution(); Object.defineProperty(navigator, 'userAgent', { value: userAgent, writable: false }); Object.defineProperties(screen, { width: { value: width, writable: false }, height: { value: height, writable: false } }); } } // 使用动态指纹 const fingerprint = new FingerprintSpoofing(); fingerprint.apply(); 19. 检测逃逸技术 // 检测是否被检测 function detectFingerprinting() { const suspiciousAPIs = [ 'toDataURL', 'getImageData', 'measureText', 'getParameter', 'createAnalyser', 'createOscillator', 'getBattery' ]; let detectionCount = 0; suspiciousAPIs.forEach(api => { const elements = document.querySelectorAll('script'); elements.forEach(script => { if (script.textContent && script.textContent.includes(api)) { detectionCount++; console.warn(`检测到指纹识别API: ${api}`); } }); }); if (detectionCount > 3) { console.warn('检测到大量指纹识别代码，建议启用防护'); return true; } return false; } // 自动启用防护 if (detectFingerprinting()) { console.log('自动启用指纹防护'); // 执行防护代码 }
Win11 22H2以上版本首次启动跳过联网和配置本地账户教程
装机完成以后，首次启动时需要断网(台式机记得拔网线)，然后在首次启动出现联网界面时按下Shift+F10，输入oobe\bypassnro并回车，此时系统会自动重启，重启后就可以离线配置 如果上述方案无效，可以尝试断网模式下，在首次启动出现联网界面时按下Shift+F10调出命令行，输入「start ms-cxh:localonly」就可以直接跳过联网账户登录，比目前其他需要更改注册表的方法要简单许多。
准备工作 此脚本支持centos系列有yum命令的系统 可以访问外网的网卡 移除虚拟机原有的yum源（可删除或移走） 使用脚本直接安装 #!/bin/bash #version=v1 ########## #关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 if [ "$EUID" -ne 0 ]; then echo -e "\033[31m请使用root权限运行此脚本！\033[0m" exit 1 fi if [ ! -f /etc/yum.repos.d/centos.repo ]; then # 如果文件不存在，打印创建信息 echo "创建 /etc/yum.repos.d/centos.repo文件..." # 使用网络传输下载阿里云yum源 curl http://mirrors.aliyun.com/repo/Centos-7.repo -o /etc/yum.repos.d/centos.repo # 将文件内的http改成https，使用安全访问 sed -i 's/http/https/g' /etc/yum.repos.d/centos.repo # 更新 yum clean all && yum repolist else # 如果文件已存在，打印警告信息 echo -e "\033[32m警告：/etc/yum.repos.d/centos.repo 文件已存在，不会进行修改。\033[0m" fi if [ ! -f /etc/yum.repos.d/docker.repo ]; then # 如果文件不存在，打印创建信息 echo "创建 /etc/yum.repos.d/docker-ce.repo 文件..." # 安装 wget 工具（如果尚未安装） yum -y install wget # 从阿里云镜像下载 Docker CE 的 yum 仓库配置文件 wget -O /etc/yum.repos.d/docker-ce.repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 更新 yum 缓存 yum makecache else # 如果文件已存在，打印警告信息 echo -e "\033[0m 警告：/etc/yum.repos.d/docker.repo 文件已存在，不会进行修改。\033[0m" fi ########## # 输出提示信息 a="如果之前有安装docker的话，先删除docker" echo -e "\033[31m$a\033[0m" # 检查是否安装了 Docker if [ -x "$(command -v docker)" ]; then # 停止 Docker 服务 echo -e "\033[32m正在停止 Docker 服务...\033[0m" if systemctl is-active --quiet docker; then sudo systemctl stop docker else echo -e "\033[33mDocker 服务未运行，跳过停止步骤。\033[0m" fi # 停止 Docker 套接字 if systemctl is-active --quiet docker.socket; then echo -e "\033[32m正在停止 Docker 套接字...\033[0m" sudo systemctl stop docker.socket else echo -e "\033[33mDocker 套接字未运行，跳过停止步骤。\033[0m" fi # 禁用 Docker 服务 if systemctl is-enabled --quiet docker; then echo -e "\033[32m正在禁用 Docker 服务...\033[0m" sudo systemctl disable docker else echo -e "\033[33mDocker 服务未启用，跳过禁用步骤。\033[0m" fi # 禁用 Docker 套接字 if systemctl is-enabled --quiet docker.socket; then echo -e "\033[32m正在禁用 Docker 套接字...\033[0m" sudo systemctl disable docker.socket else echo -e "\033[33mDocker 套接字未启用，跳过禁用步骤。\033[0m" fi # 卸载 Docker 相关包 echo -e "\033[32m正在卸载 Docker 相关包...\033[0m" sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine \ docker-ce \ docker-ce-cli \ ontainerd.io \ docker-buildx-plugin \ docker-socket \ docker-compose-plugin \ docker-ce-rootless-extras # 删除 Docker 数据目录 echo -e "\033[32m正在删除 Docker 数据目录 /var/lib/docker...\033[0m" sudo rm -rf /var/lib/docker # 删除 Docker 配置目录 echo -e "\033[32m正在删除 Docker 配置目录 /etc/docker...\033[0m" sudo rm -rf /etc/docker # 删除 Docker.sock数据目录 echo -e "\033[32m正在删除 Docker.sock 数据目录/var/run/docker.sock\033[0m" sudo rm -rf /var/run/docker.sock # 删除 Docker 用户组 if getent group docker > /dev/null; then echo -e "\033[32m正在删除 Docker 用户组...\033[0m" sudo groupdel docker fi echo -e "\033[32mDocker 已成功删除。\033[0m" else echo -e "\033[31mDocker 未安装，无需删除。\033[0m" fi ########## b=使用yum安装docker echo -e "\033[31m$b\033[0m" echo -e "Usage : $0 \033[1;31m 1(安装最新版) 2(选择docker版本) 3(不安装docker,只安装docker compose)\033[0m" read -p "选择: " a case $a in 1) echo -e "\033[32m 正在安装最新版docker... \033[0m" yum -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin systemctl start docker systemctl enable docker echo -e "\033[32m 已经安装并且启动最新版docker. \033[0m" systemctl status docker ;; 2) #获取所有可用Docker CE版本 get_docker_versions() { yum list docker-ce --showduplicates | sort -r | awk '{print $2}' | grep -oP '(?<=:)[^:]+$' | uniq } install_specific_docker() { install_docker_repo echo -e "\033[31m请选择你想要安装的Docker版本号：\033[0m" } versions=$(get_docker_versions) PS3="请输入版本号(或者输入'q'退出): " select version in $versions "q"; do if $version == "q" ; then echo -e "\033[31m 退出选择。\033[0m" break elif -n "$version" ; then echo -e "\033[32m 正在安装Docker $version版本...\033[0m" yum -y install docker-ce-$version docker-ce-cli-$version containerd.io-$version systemctl start docker systemctl enable docker echo -e "\033[32m Docker版本$version已经安装完毕并且启动。 \033[0m" systemctl status docker break else echo -e "\033[31m 无效选择，请重新输入。 \033[0m" fi done ;; 3) echo -e "\033[31m 退出docker安装。\033[0m" ;; *) 不符合以上模式执行的命令 esac ########## if ! command -v docker &> /dev/null ; then echo -e "\033[31m没有安装docker服务 \033[0m" elif [ ! -f /etc/docker/daemon.json ] ; then echo -e "\033[32m创建 /etc/docker/daemon.json 文件... \033[0m" cat <<EOF > /etc/docker/daemon.json { "registry-mirrors": [ "https://docker.registry.cyou", "https://docker-cf.registry.cyou", "https://dockercf.jsdelivr.fyi", "https://docker.jsdelivr.fyi", "https://dockertest.jsdelivr.fyi", "https://mirror.aliyuncs.com", "https://dockerproxy.com", "https://mirror.baidubce.com", "https://docker.m.daocloud.io", "https://docker.nju.edu.cn", "https://docker.mirrors.sjtug.sjtu.edu.cn", "https://docker.mirrors.ustc.edu.cn", "https://mirror.iscas.ac.cn", "https://docker.rainbond.c" ] } EOF systemctl daemon-reload && systemctl restart docker else echo -e "\033[31m 警告：/etc/docker/daemon.json 文件已存在，不会进行修改。\033[0m" fi ########## echo -e "\033[31m如果之前有安装docker compose的话，先删除docker compose\033[0m" # 尝试停止并移除所有由Docker Compose管理的服务 if command -v docker-compose &> /dev/null; then # 查找包含 docker-compose.yml 或 docker-compose.yaml 的目录 compose_dir=$(find . -type f -name 'docker-compose.yml' -o -name 'docker-compose.yaml' -exec dirname {} \; | head -n 1) if [ -z "$compose_dir" ]; then echo -e "\033[33m未找到 docker-compose.yml 或 docker-compose.yaml 文件，跳过 docker-compose down 步骤。\033[0m" else echo -e "\033[32m正在停止并移除Docker Compose服务... \033[0m" cd "$compose_dir" docker-compose down -v fi else echo -e "\033[33m未找到docker-compose命令，跳过docker-compose down步骤。\033[0m" fi #Author: 小宇学IT ##Website: https://blog.csdn.net/2402_82681173?type=blog # 清理Docker系统 echo -e "\033[31m正在清理Docker系统...\033[0m" docker system prune -f # 删除Docker Compose二进制文件 echo -e "\033[32m正在删除Docker Compose二进制文件... \033[0m" rm -rf /usr/local/bin/docker-compos ########## # 获取 Docker Engine 的版本 docker_version=$(docker --version | grep -oP '(\d+\.\d+\.\d+)' || echo "0.0.0") # 检查 Docker 是否已安装 if ! [ -x "$(command -v docker)" ]; then echo -e "\033[31m没有安装docker服务，先安装docker服务. \033[0m" exit 1 fi # 输出当前 Docker 版本 echo "Current Docker Engine version: $docker_version" # 定义安装函数 install_compose() { local compose_version=$1 echo -e "\033[32m正在下载docker compose版本${compose_version}... \033[0m" # 删除旧的软链接（如果存在） echo -e "\033[32m正在删除Docker Compose之前的软链接... \033[0m" sudo unlink /usr/local/bin/docker-compose sudo unlink /usr/bin/docker-compose # 下载指定版本的 Docker Compose wget https://mirror.ghproxy.com/https://github.com/docker/compose/releases/download/${compose_version}/docker-compose-$(uname -s)-$(uname -m) -O /usr/local/bin/docker-compose # 设置执行权限 chmod +x /usr/local/bin/docker-compose # 创建软链接 sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose } # 根据 Docker 版本来选择并安装对应的 Docker Compose 版本 if [[ "$docker_version" > "25.9.9" && "$docker_version" < "27.1.0" ]]; then install_compose "v2.26.0" elif [[ "$docker_version" > "24.9.9" && "$docker_version" < "26.1.0" ]]; then install_compose "v2.25.0" elif [[ "$docker_version" > "21.9.9" && "$docker_version" < "25.1.0" ]]; then install_compose "v2.2.3" elif [[ "$docker_version" > "20.9.9" && "$docker_version" < "22.0.0" ]]; then install_compose "1.29.0" elif [[ "$docker_version" > "19.9.9" && "$docker_version" < "21.0.0" ]]; then install_compose "1.27.0" elif [[ "$docker_version" > "18.9.9" && "$docker_version" < "20.0.0" ]]; then install_compose "1.24.0" elif [[ "$docker_version" > "17.9.9" && "$docker_version" < "19.0.0" ]]; then install_compose "1.22.0" elif [[ "$docker_version" > "16.9.9" && "$docker_version" < "18.0.0" ]]; then install_compose "1.18.0" else echo -e "\033[31m不支持的Docker版本: $docker_version \033[0m " exit 1 fi # 验证 Docker Compose 安装 docker_compose_version=$(docker-compose --version | grep -oP '(\d+\.\d+\.\d+)' || echo "0.0.0") # 检查 Docker Compose 是否安装成功 if command -v docker-compose &> /dev/null; then echo -e "\033[32mDocker Compose ${docker_compose_version} 安装成功。\033[0m" d=$(docker-compose --version) echo -e "\033[32m$d\033[0m" else echo -e "\033[31mDocker Compose安装失败。\033[0m" exit 1 fi
版本一 重点监控某些 IP 地址的多次失败登录尝试，并针对特定 IP 发送告警，该脚本会监控并告警那些尝试登录次数超过阈值的 IP 地址 #!/bin/bash # 设置钉钉 webhook URL 改为自己的钉钉机器人webhook WEBHOOK_URL="https://oapi.dingtalk.com/robot/send?access_token=your_access_token" # 设置日志文件路径，默认是 /var/log/auth.log（具体路径根据你的系统调整） LOG_FILE="/var/log/auth.log" # 设置匹配的异常登录模式，例如：多个错误登录尝试或 IP 地址 # 这里假设我们匹配 "Failed password" 登录尝试 PATTERN="Failed password" # 设置阈值，超过此次数的 IP 会触发告警 THRESHOLD=5 # 获取当前时间，格式化为 YYYY-MM-DD HH:MM:SS CURRENT_TIME=$(date "+%Y-%m-%d %H:%M:%S") # 提取失败登录的 IP 地址，并统计每个 IP 的尝试次数 IP_ATTEMPTS=$(grep "$PATTERN" $LOG_FILE | grep -oP 'from \K(\S+)' | sort | uniq -c | sort -nr) # 检查每个 IP 是否超过阈值，若超过则触发告警 ALERT_IPS="" while read -r count ip; do if [ "$count" -gt "$THRESHOLD" ]; then ALERT_IPS="$ALERT_IPS\nIP 地址 $ip 尝试登录次数：$count 次" fi done <<< "$IP_ATTEMPTS" # 如果有 IP 地址超过阈值，则发送钉钉告警 if [ -n "$ALERT_IPS" ]; then # 构造告警内容 MESSAGE="警告：检测到以下 IP 地址的 SSH 异常登录尝试。\n当前时间：$CURRENT_TIME\n$ALERT_IPS" # 发送钉钉告警 curl -X POST $WEBHOOK_URL \ -H "Content-Type: application/json" \ -d '{ "msgtype": "text", "text": { "content": "'"$MESSAGE"'" } }' fi 版本二 为了避免频繁的告警，新增加一个机制，确保只有当出现 新的异常攻击 IP 地址 时才触发告警。具体思路是： 记录上次已经告警过的 IP 地址：可以通过保存一个文件，记录上次已经触发告警的 IP 地址 检查新增的攻击 IP 地址：在每次运行时，比较当前的异常登录 IP 地址和上次记录的 IP 地址，只有新增的 IP 地址才会触发告警 更新已告警的 IP 地址列表：告警后，将新增的 IP 地址加入到记录文件中，避免重复告警 #!/bin/bash # 设置钉钉 webhook URL WEBHOOK_URL="https://oapi.dingtalk.com/robot/send?access_token=your_access_token" # 设置日志文件路径，默认是 /var/log/auth.log（具体路径根据你的系统调整） LOG_FILE="/var/log/auth.log" # 设置匹配的异常登录模式，例如：多个错误登录尝试或 IP 地址 PATTERN="Failed password" # 设置阈值，超过此次数的 IP 会触发告警 THRESHOLD=5 # 获取当前时间，格式化为 YYYY-MM-DD HH:MM:SS CURRENT_TIME=$(date "+%Y-%m-%d %H:%M:%S") # 提取失败登录的 IP 地址，并统计每个 IP 的尝试次数 IP_ATTEMPTS=$(grep "$PATTERN" $LOG_FILE | grep -oP 'from \K(\S+)' | sort | uniq -c | sort -nr) # 临时存储上次告警过的 IP 地址 LAST_ALERT_FILE="/tmp/last_alert_ips.txt" # 读取上次告警过的 IP 地址列表 if [ -f "$LAST_ALERT_FILE" ]; then LAST_ALERT_IPS=$(cat "$LAST_ALERT_FILE") else LAST_ALERT_IPS="" fi # 检查每个 IP 是否超过阈值，若超过则触发告警 ALERT_IPS="" NEW_ALERT_IPS="" while read -r count ip; do if [ "$count" -gt "$THRESHOLD" ]; then if ! "$LAST_ALERT_IPS" =~ "$ip" ; then # 记录新增的攻击 IP 地址 NEW_ALERT_IPS="$NEW_ALERT_IPS\n*IP 地址* [**$ip**] 尝试登录次数：$count 次" fi fi done <<< "$IP_ATTEMPTS" # 如果有新增 IP 地址，发送钉钉告警 if [ -n "$NEW_ALERT_IPS" ]; then # 构造告警内容 MESSAGE="警告：检测到以下 IP 地址的 SSH 异常登录尝试。\n当前时间：$CURRENT_TIME\n$NEW_ALERT_IPS" # 发送钉钉告警 curl -X POST $WEBHOOK_URL \ -H "Content-Type: application/json" \ -d '{ "msgtype": "text", "text": { "content": "'"$MESSAGE"'" } }' # 更新已告警的 IP 地址记录 echo -e "$LAST_ALERT_IPS\n$NEW_ALERT_IPS" | grep -oP 'from \K(\S+)' | sort | uniq > "$LAST_ALERT_FILE" fi 版本三 要在脚本中新增将异常 IP 地址拉入黑名单的功能，可以在发现异常 IP 地址时，自动执行相关的封禁操作，将这些 IP 地址拉入黑名单，防止其继续尝试登录。 以下是修改后的脚本，使用 iptables 来封禁异常的 IP 地址 #!/bin/bash # 设置钉钉 webhook URL WEBHOOK_URL="https://oapi.dingtalk.com/robot/send?access_token=your_access_token" # 设置日志文件路径，默认是 /var/log/auth.log（具体路径根据你的系统调整） LOG_FILE="/var/log/auth.log" # 设置匹配的异常登录模式，例如：多个错误登录尝试或 IP 地址 PATTERN="Failed password" # 设置阈值，超过此次数的 IP 会触发告警 THRESHOLD=5 # 获取当前时间，格式化为 YYYY-MM-DD HH:MM:SS CURRENT_TIME=$(date "+%Y-%m-%d %H:%M:%S") # 提取失败登录的 IP 地址，并统计每个 IP 的尝试次数 IP_ATTEMPTS=$(grep "$PATTERN" $LOG_FILE | grep -oP 'from \K(\S+)' | sort | uniq -c | sort -nr) # 临时存储上次告警过的 IP 地址 LAST_ALERT_FILE="/tmp/last_alert_ips.txt" # 读取上次告警过的 IP 地址列表 if [ -f "$LAST_ALERT_FILE" ]; then LAST_ALERT_IPS=$(cat "$LAST_ALERT_FILE") else LAST_ALERT_IPS="" fi # 检查每个 IP 是否超过阈值，若超过则触发告警 ALERT_IPS="" NEW_ALERT_IPS="" BLACKLIST_IPS="" while read -r count ip; do if [ "$count" -gt "$THRESHOLD" ]; then if ! "$LAST_ALERT_IPS" =~ "$ip" ; then # 记录新增的攻击 IP 地址 NEW_ALERT_IPS="$NEW_ALERT_IPS\n*IP 地址* [**$ip**] 尝试登录次数：$count 次" # 将此 IP 地址加入黑名单 BLACKLIST_IPS="$BLACKLIST_IPS\n$ip" # 使用 iptables 封禁该 IP iptables -A INPUT -s "$ip" -j DROP echo "IP 地址 $ip 已被添加到黑名单并封禁。" fi fi done <<< "$IP_ATTEMPTS" # 如果有新增 IP 地址，发送钉钉告警 if [ -n "$NEW_ALERT_IPS" ]; then # 构造告警内容 MESSAGE="警告：检测到以下 IP 地址的 SSH 异常登录尝试。\n当前时间：$CURRENT_TIME\n$NEW_ALERT_IPS" # 发送钉钉告警 curl -X POST $WEBHOOK_URL \ -H "Content-Type: application/json" \ -d '{ "msgtype": "text", "text": { "content": "'"$MESSAGE"'" } }' # 更新已告警的 IP 地址记录 echo -e "$LAST_ALERT_IPS\n$NEW_ALERT_IPS" | grep -oP 'from \K(\S+)' | sort | uniq > "$LAST_ALERT_FILE" fi # 如果有 IP 被加入黑名单，打印日志 if [ -n "$BLACKLIST_IPS" ]; then echo -e "以下 IP 地址已被添加到黑名单并封禁：$BLACKLIST_IPS" >> /var/log/ssh_blacklist.log fi 最后编写一个crontab任务即可
完整脚本,建议保存为docker-proxy.sh #!/bin/bash # ===================== 用户配置区 ===================== # ▼▼▼ 以下变量必须修改 ▼▼▼ HTTP_PROXY="http://192.168.1.118:7890/" # 改为你的代理IP和端口 HTTPS_PROXY="http://192.168.1.118:7890/" # 同上 NO_PROXY="localhost,127.0.0.1,.corp.com" # 内网域名白名单 # ▲▲▲ 修改结束 ▲▲▲ # ===================== 执行逻辑 ===================== if [ $# -eq 0 ]; then echo "错误：请指定操作模式" echo "用法：sudo $0 {set|remove}" exit 1 fi # 创建配置目录 sudo mkdir -p /etc/systemd/system/docker.service.d/ case "$1" in "set") echo "▶ 正在注入代理配置（目标地址：${HTTP_PROXY}）..." # 生成代理配置文件 sudo tee /etc/systemd/system/docker.service.d/proxy.conf > /dev/null <<EOF [Service] Environment="HTTP_PROXY=${HTTP_PROXY}" Environment="HTTPS_PROXY=${HTTPS_PROXY}" Environment="NO_PROXY=${NO_PROXY}" EOF # 重载服务配置 sudo systemctl daemon-reload sudo systemctl restart docker echo "✔ 代理已生效！" echo " 验证命令：docker info | grep -i proxy" ;; "remove") echo "▶ 正在清除代理配置..." sudo rm -f /etc/systemd/system/docker.service.d/proxy.conf sudo systemctl daemon-reload sudo systemctl restart docker echo "✔ 已恢复原始环境" ;; *) echo "错误：无效参数，支持 set 或 remove" exit 1 ;; esac echo " 注意：若容器网络异常，请尝试重启容器！" 操作指南 chmod +x docker-proxy.sh 修改配置 将192.168.1.118:7890替换为实际代理地址 白名单按需添加（如.alibaba.com、.docker.io） 执行命令 # 设置代理 ./docker-proxy.sh set # 清除代理 ./docker-proxy.sh remove
docker部署rabbitmq:3-management配置消息持久化 version: '3' services: rabbit: image: rabbitmq:3-management container_name: "rabbitmq" hostname: rabbitmq # 持久化必须有，否则重启会重置hostname，持久化会失败 restart: always ports: - "5672:5672" - "15672:15672" environment: - RABBITMQ_NODENAME: rabbit@localhost # 持久化必须有，否则重启会重置nodename，持久化会失败 - RABBITMQ_DEFAULT_USER: guest - RABBITMQ_DEFAULT_PASS: *** # 根据实际情况设置 - RABBITMQ_DEFAULT_VHOST: *** # 根据实际情况设置 volumes: - ./data:/var/lib/rabbitmq - ./10-defaults.conf:/etc/rabbitmq/conf.d/10-defaults.conf # 更改管理界面路由 networks: - localnet networks: localnet: external: true name: localnet driver: bridge 更改管理界面路由 ## DEFAULT SETTINGS ARE NOT MEANT TO BE TAKEN STRAIGHT INTO PRODUCTION ## see https://www.rabbitmq.com/configure.html for further information ## on configuring RabbitMQ ## allow access to the guest user from anywhere on the network ## https://www.rabbitmq.com/access-control.html#loopback-users ## https://www.rabbitmq.com/production-checklist.html#users loopback_users.guest = false ## Send all logs to stdout/TTY. Necessary to see logs when running via ## a container log.console = true # 最重要的是这条,配置并挂载到容器内部以后，访问管理界面url由原来的http://IP:15672变成http://IP:15672/rabbit/ management.path_prefix = /rabbit
写了一个新的小脚本，用于管理linux用户 #!/bin/bash # Linux用户管理脚本 - 支持CentOS、Rocky Linux和Ubuntu系统 # 颜色设置 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' # 初始化 init() { # 检查root权限 [ "$(id -u)" -ne 0 ] && echo -e "${RED}错误: 需要root权限${NC}" && exit 1 # 检测Linux发行版 if [ -f /etc/os-release ]; then . /etc/os-release OS=$NAME if $OS == *"CentOS"* || $OS == *"Rocky"* ; then PACKAGE_MANAGER="yum" USER_ADD_CMD="useradd" GROUP_ADD_CMD="groupadd" elif $OS == *"Ubuntu"* || $OS == *"Debian"* ; then PACKAGE_MANAGER="apt" USER_ADD_CMD="adduser" GROUP_ADD_CMD="addgroup" else echo -e "${YELLOW}警告: 未知的Linux发行版: $OS, 使用通用命令${NC}" PACKAGE_MANAGER="apt" USER_ADD_CMD="useradd" GROUP_ADD_CMD="groupadd" fi else echo -e "${RED}错误: 无法检测操作系统${NC}" && exit 1 fi echo -e "${GREEN}检测到: $OS${NC}" } # 检查用户是否存在 user_exists() { id "$1" &>/dev/null return $? } # 检查组是否存在 group_exists() { grep -q "^$1:" /etc/group return $? } # 添加用户 add_user() { read -p "用户名: " username user_exists "$username" && echo -e "${RED}用户已存在${NC}" && return 1 read -p "主目录(默认/home/$username): " homedir homedir=${homedir:-/home/$username} read -p "Shell(默认/bin/bash): " usershell usershell=${usershell:-/bin/bash} read -p "密码: " userpass if $PACKAGE_MANAGER == "apt" ; then $USER_ADD_CMD $username --home $homedir --shell $usershell echo "$username:$userpass" | chpasswd else $USER_ADD_CMD -m -d $homedir -s $usershell $username echo "$userpass" | passwd --stdin $username fi [ $? -eq 0 ] && echo -e "${GREEN}用户创建成功${NC}" || echo -e "${RED}创建失败${NC}" read -p "添加sudo权限? (y/n): " add_sudo [[ $add_sudo == [yY] ]] && set_sudo_permission "$username" } # 删除用户 delete_user() { read -p "要删除的用户名: " username user_exists "$username" || { echo -e "${RED}用户不存在${NC}"; return 1; } read -p "同时删除主目录? (y/n): " del_home [[ $del_home == [yY] ]] && userdel -r $username || userdel $username [ $? -eq 0 ] && echo -e "${GREEN}用户已删除${NC}" || echo -e "${RED}删除失败${NC}" } # 修改用户 modify_user() { read -p "要修改的用户名: " username user_exists "$username" || { echo -e "${RED}用户不存在${NC}"; return 1; } echo -e "1.修改主目录 2.修改shell 3.修改用户组 4.返回" read -p "选择: " choice case $choice in 1) read -p "新主目录: " new_home usermod -d $new_home $username && echo -e "${GREEN}修改成功${NC}" || echo -e "${RED}修改失败${NC}" ;; 2) read -p "新shell: " new_shell usermod -s $new_shell $username && echo -e "${GREEN}修改成功${NC}" || echo -e "${RED}修改失败${NC}" ;; 3) read -p "新用户组: " new_group if ! group_exists "$new_group"; then read -p "组不存在, 是否创建? (y/n): " create_group [[ $create_group == [yY] ]] && $GROUP_ADD_CMD $new_group || return 1 fi usermod -g $new_group $username && echo -e "${GREEN}修改成功${NC}" || echo -e "${RED}修改失败${NC}" ;; 4) return 0 ;; *) ;; esac } # 列出所有用户 list_users() { echo -e "${BLUE}系统用户:${NC}" echo -e "${YELLOW}UID 用户名 主目录 Shell${NC}" echo "-------------------------------" awk -F: '$3 >= 1000 && $3 < 65534 {print $3, $1, $6, $7}' /etc/passwd | sort -n | while read uid username homedir shell; do echo -e "${GREEN}$uid $username $homedir $shell${NC}" done echo -e "\n${BLUE}用户总数: $(awk -F: '$3 >= 1000 && $3 < 65534 {count++} END {print count}' /etc/passwd)${NC}" } # 添加/删除用户组 manage_group() { local action=$1 if [ "$action" = "add" ]; then read -p "新用户组名: " groupname group_exists "$groupname" && echo -e "${RED}组已存在${NC}" && return 1 $GROUP_ADD_CMD $groupname else read -p "要删除的用户组: " groupname group_exists "$groupname" || { echo -e "${RED}组不存在${NC}"; return 1; } groupdel $groupname fi [ $? -eq 0 ] && echo -e "${GREEN}操作成功${NC}" || echo -e "${RED}操作失败${NC}" } # 修改用户密码 change_password() { read -p "用户名: " username user_exists "$username" || { echo -e "${RED}用户不存在${NC}"; return 1; } read -p "新密码: " userpass if $PACKAGE_MANAGER == "apt" ; then echo "$username:$userpass" | chpasswd else echo "$userpass" | passwd --stdin $username fi [ $? -eq 0 ] && echo -e "${GREEN}密码已修改${NC}" || echo -e "${RED}修改失败${NC}" } # 设置sudo权限 set_sudo_permission() { local username=$1 if [ -z "$username" ]; then read -p "用户名: " username user_exists "$username" || { echo -e "${RED}用户不存在${NC}"; return 1; } fi # 安装sudo（如需） if ! command -v sudo &> /dev/null; then echo -e "${YELLOW}安装sudo...${NC}" $PACKAGE_MANAGER == "apt" && { apt update && apt install -y sudo; } || yum install -y sudo fi # 添加权限 if $PACKAGE_MANAGER == "apt" ; then usermod -aG sudo $username grep -q "^wheel:" /etc/group && usermod -aG wheel $username else usermod -aG wheel $username fi # 设置无密码sudo read -p "允许无密码sudo? (y/n): " nopass if [[ $nopass == [yY] ]]; then echo "$username ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/$username chmod 0440 /etc/sudoers.d/$username fi echo -e "${GREEN}sudo权限已设置${NC}" } # 显示用户信息 show_user_info() { read -p "用户名(留空显示所有): " username [ -z "$username" ] && { list_users; return 0; } user_exists "$username" || { echo -e "${RED}用户不存在${NC}"; return 1; } # 获取信息 uid=$(id -u $username) gid=$(id -g $username) groups=$(id -nG $username) homedir=$(grep "^$username:" /etc/passwd | cut -d: -f6) shell=$(grep "^$username:" /etc/passwd | cut -d: -f7) # 检查sudo权限和账户状态 groups $username | grep -qE "\b(sudo|wheel)\b" && sudo_status="${GREEN}已授权${NC}" || sudo_status="${RED}未授权${NC}" grep -q "^$username:!!" /etc/shadow 2>/dev/null && account_status="${RED}已锁定${NC}" || account_status="${GREEN}正常${NC}" # 显示信息 echo -e "${BLUE}用户信息:${NC}" echo -e "UID: $uid\nGID: $gid\n主目录: $homedir\nShell: $shell\n组: $groups\nsudo: $sudo_status\n状态: $account_status" echo -e "\n${BLUE}最后登录:${NC}" lastlog -u $username } # 主函数 main() { init while true; do clear echo echo -e "${BLUE}╭────────────────────────────────╮${NC}" echo -e "${BLUE}│${NC} ${GREEN}Linux 用户管理系统${NC} ${BLUE}│${NC}" echo -e "${BLUE}╰────────────────────────────────╯${NC}" echo echo -e " ${GREEN}[1]${NC} 添加用户 ${GREEN}[2]${NC} 删除用户 ${GREEN}[3]${NC} 修改用户" echo -e " ${GREEN}[4]${NC} 查看用户 ${GREEN}[5]${NC} 添加用户组 ${GREEN}[6]${NC} 删除用户组" echo -e " ${GREEN}[7]${NC} 修改密码 ${GREEN}[8]${NC} 设置sudo ${GREEN}[9]${NC} 用户信息" echo -e " ${RED}[0]${NC} 退出系统" echo echo -e "${BLUE}──────────────────────────────────────────────${NC}" read -p " 请选择操作 [0-9]: " choice echo case $choice in 1) add_user ;; 2) delete_user ;; 3) modify_user ;; 4) list_users ;; 5) manage_group "add" ;; 6) manage_group "del" ;; 7) change_password ;; 8) set_sudo_permission ;; 9) show_user_info ;; 0) clear; exit 0 ;; *) ;; esac echo read -p " 按回车键继续..." dummy done } # 启动脚本 main
Redis BitMap是一种高效的位操作数据结构，它将字符串看作是由二进制位组成的数组。 在Redis中，一个BitMap最大可存储2^32个位，约512MB，而操作单个位的时间复杂度为O(1)。 这种结构在处理海量数据的布尔型状态时尤其高效，能在极小的内存占用下完成高性能的统计与分析任务。 Redis BitMap基础 基本概念 BitMap本质上是一个位数组，数组的每个元素只能是0或1。在Redis中，BitMap是基于String类型实现的，一个字符串的每个字节(8位)可以表示8个不同位，从而实现了位数组的功能 核心命令 Redis提供了一系列操作BitMap的命令： SETBIT key offset value : 设置key在offset处的位值 GETBIT key offset : 获取key在offset处的位值 BITCOUNT key [start end] : 统计指定范围内1的数量 BITPOS key bit [start end] : 返回第一个被设置为bit值的位的位置 BITOP operation destkey key [key ...] : 对多个BitMap执行位操作(AND, OR, XOR, NOT) BITFIELD key [GET type offset] [SET type offset value] : 原子操作多个位域 应用场景1：用户签到系统 场景描述 在许多应用中，需要记录用户每天是否签到，并支持查询用户连续签到天数、当月签到总天数等统计功能。 传统的方案可能使用关系型数据库存储每日签到记录，但这种方式既耗费存储空间，查询效率也低。 BitMap解决方案 使用BitMap，我们可以用一个位表示一天的签到状态，一个月只需30-31位，非常节省空间。 实现示例 import redis.clients.jedis.Jedis; import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class SignInSystem { private Jedis jedis; private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyyMM"); public SignInSystem(String host, int port) { this.jedis = new Jedis(host, port); } // 用户签到 public void signIn(long userId, LocalDate date) { String signKey = getSignKey(userId, date); int dayOfMonth = date.getDayOfMonth() - 1; // Redis BitMap是0-based jedis.setbit(signKey, dayOfMonth, true); } // 检查用户是否签到 public boolean hasSignedIn(long userId, LocalDate date) { String signKey = getSignKey(userId, date); int dayOfMonth = date.getDayOfMonth() - 1; return jedis.getbit(signKey, dayOfMonth); } // 获取用户当月签到次数 public long getMonthlySignCount(long userId, LocalDate date) { String signKey = getSignKey(userId, date); return jedis.bitcount(signKey); } // 获取用户当月首次签到日期 public int getFirstSignInDay(long userId, LocalDate date) { String signKey = getSignKey(userId, date); long pos = jedis.bitpos(signKey, true); return pos == -1 ? -1 : (int) pos + 1; // 转换回自然日 } // 获取用户当月连续签到天数 public int getConsecutiveSignDays(long userId, LocalDate date) { String signKey = getSignKey(userId, date); int dayOfMonth = date.getDayOfMonth() - 1; int count = 0; // 从当天开始向前查找连续签到的天数 for (int i = dayOfMonth; i >= 0; i--) { if (jedis.getbit(signKey, i)) { count++; } else { break; } } return count; } // 构建签到Key private String getSignKey(long userId, LocalDate date) { return "user:sign:" + userId + ":" + date.format(MONTH_FORMATTER); } } 性能与空间分析 空间占用 : 每个用户每月仅需4字节(1个整型)就能存储所有签到记录 时间复杂度 : 单次签到/查询操作为O(1) 优势 : 极低的存储成本，高效的统计能力 应用场景2：在线用户统计 场景描述 大型系统需要实时统计在线用户数，及分析用户活跃情况，如日活跃用户数(DAU)、月活跃用户数(MAU)等关键指标。传统方案可能使用Set或Hash结构，但面对海量用户时会消耗大量内存。 BitMap解决方案 使用BitMap，用户ID可以直接映射为位偏移量，每个用户只占用1位。一千万用户只需约1.2MB内存。 实现示例 import redis.clients.jedis.Jedis; import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class UserActivityTracker { private Jedis jedis; private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); public UserActivityTracker(String host, int port) { this.jedis = new Jedis(host, port); } // 记录用户活跃 public void trackUserActivity(long userId, LocalDate date) { String key = getActivityKey(date); jedis.setbit(key, userId, true); } // 获取日活跃用户数(DAU) public long getDailyActiveUsers(LocalDate date) { String key = getActivityKey(date); return jedis.bitcount(key); } // 获取月活跃用户数(MAU) public long getMonthlyActiveUsers(int year, int month) { LocalDate startDate = LocalDate.of(year, month, 1); LocalDate endDate = startDate.plusMonths(1).minusDays(1); // 创建临时结果键 String destKey = "temp:mau:" + year + month; // 收集整月的所有日期的活跃用户 for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) { String dayKey = getActivityKey(date); // 使用OR操作合并日活跃数据 jedis.bitop("OR", destKey, destKey, dayKey); } // 计算总活跃用户数 long mau = jedis.bitcount(destKey); // 清理临时键 jedis.del(destKey); return mau; } // 判断两天的活跃用户重合度 (留存率相关) public long getActiveUserOverlap(LocalDate date1, LocalDate date2) { String key1 = getActivityKey(date1); String key2 = getActivityKey(date2); String destKey = "temp:overlap:" + date1.format(DATE_FORMATTER) + ":" + date2.format(DATE_FORMATTER); // 使用AND操作找出两天都活跃的用户 jedis.bitop("AND", destKey, key1, key2); long overlap = jedis.bitcount(destKey); // 清理临时键 jedis.del(destKey); return overlap; } // 获取活跃用户Key private String getActivityKey(LocalDate date) { return "user:active:" + date.format(DATE_FORMATTER); } } 拓展：次日留存率计算 public double getRetentionRate(LocalDate date) { LocalDate nextDate = date.plusDays(1); // 当天活跃用户数 long todayActive = getDailyActiveUsers(date); if (todayActive == 0) return 0.0; // 计算当天活跃用户中第二天仍活跃的用户数 long overlap = getActiveUserOverlap(date, nextDate); // 计算留存率 return (double) overlap / todayActive; } 应用场景3：布隆过滤器实现 场景描述 布隆过滤器是一种空间效率高的概率性数据结构，用于判断元素是否存在于集合中。它在大数据、缓存穿透防护、垃圾邮件过滤等场景中广泛应用。 布隆过滤器可能存在误判，但它能以极小的内存代价完成高效的查询。 BitMap解决方案 使用Redis的BitMap可以轻松实现布隆过滤器，通过多个哈希函数将元素映射到位数组的不同位置。 实现示例 import redis.clients.jedis.Jedis; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; public class RedisBloomFilter { private Jedis jedis; private String key; private int hashFunctions; private long size; /** * 创建布隆过滤器 * @param host Redis主机 * @param port Redis端口 * @param key 过滤器键名 * @param size 位数组大小 * @param hashFunctions 哈希函数数量 */ public RedisBloomFilter(String host, int port, String key, long size, int hashFunctions) { this.jedis = new Jedis(host, port); this.key = key; this.size = size; this.hashFunctions = hashFunctions; } /** * 添加元素到布隆过滤器 */ public void add(String value) { for (long position : getHashPositions(value)) { jedis.setbit(key, position, true); } } /** * 判断元素是否可能存在于过滤器中 * @return true表示可能存在，false表示一定不存在 */ public boolean mightContain(String value) { for (long position : getHashPositions(value)) { if (!jedis.getbit(key, position)) { return false; } } return true; } /** * 计算元素在布隆过滤器中的多个位置 */ private List<Long> getHashPositions(String value) { List<Long> positions = new ArrayList<>(hashFunctions); try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bytes = md.digest(value.getBytes(StandardCharsets.UTF_8)); // 使用同一个MD5值生成多个哈希位置 for (int i = 0; i < hashFunctions; i++) { long hashValue = 0; for (int j = i * 4; j < i * 4 + 4; j++) { hashValue <<= 8; int index = j % bytes.length; hashValue |= (bytes[index] & 0xFF); } positions.add(Math.abs(hashValue % size)); } } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5 algorithm not found", e); } return positions; } /** * 重置过滤器 */ public void clear() { jedis.del(key); } } 应用实例：缓存穿透防护 public class CacheService { private RedisBloomFilter bloomFilter; private Jedis jedis; public CacheService(String host, int port) { this.jedis = new Jedis(host, port); // 创建布隆过滤器，大小为1000万位，使用7个哈希函数 this.bloomFilter = new RedisBloomFilter(host, port, "cache:bloom:filter", 10_000_000, 7); // 初始化过滤器，添加所有有效的ID initBloomFilter(); } private void initBloomFilter() { // 模拟从数据库加载所有有效ID并添加到布隆过滤器 List<String> allValidIds = getAllIdsFromDatabase(); for (String id : allValidIds) { bloomFilter.add(id); } } public String getDataById(String id) { // 首先检查ID是否可能存在 if (!bloomFilter.mightContain(id)) { return null; // ID一定不存在，直接返回 } // 尝试从缓存获取 String cacheKey = "cache:data:" + id; String data = jedis.get(cacheKey); if (data != null) { return data; // 缓存命中 } // 缓存未命中，从数据库获取 data = getFromDatabase(id); if (data != null) { // 存入缓存 jedis.setex(cacheKey, 3600, data); return data; } // ID不存在于数据库（布隆过滤器误判的情况） return null; } // 模拟从数据库获取数据 private String getFromDatabase(String id) { // 实际项目中会查询数据库 return null; // 模拟数据不存在 } // 模拟从数据库获取所有ID private List<String> getAllIdsFromDatabase() { // 实际项目中会查询数据库获取所有有效ID return new ArrayList<>(); } } 应用场景4：用户行为分析与推荐系统 场景描述 在推荐系统中，需要分析用户对不同物品（如文章、商品）的行为偏好，包括浏览、收藏、点赞等。这些数据用于构建用户画像和内容推荐算法的输入。 传统方案可能使用关系型数据库或文档数据库存储这些行为记录，但在大规模场景下会面临存储和查询效率问题。 BitMap解决方案 使用BitMap可以高效存储用户对物品的偏好状态。例如，使用不同的BitMap记录用户是否浏览、收藏、购买某商品。 实现示例 import redis.clients.jedis.Jedis; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class UserBehaviorAnalyzer { private Jedis jedis; // 行为类型常量 private static final String VIEW = "view"; private static final String LIKE = "like"; private static final String COLLECT = "collect"; private static final String PURCHASE = "purchase"; public UserBehaviorAnalyzer(String host, int port) { this.jedis = new Jedis(host, port); } /** * 记录用户对物品的行为 * @param userId 用户ID * @param itemId 物品ID * @param behaviorType 行为类型 */ public void recordBehavior(long userId, long itemId, String behaviorType) { String key = getBehaviorKey(userId, behaviorType); jedis.setbit(key, itemId, true); } /** * 检查用户是否对物品有过特定行为 */ public boolean hasBehavior(long userId, long itemId, String behaviorType) { String key = getBehaviorKey(userId, behaviorType); return jedis.getbit(key, itemId); } /** * 获取用户对特定行为的物品总数 */ public long getBehaviorCount(long userId, String behaviorType) { String key = getBehaviorKey(userId, behaviorType); return jedis.bitcount(key); } /** * 获取有特定行为的用户总数 */ public long getUserCountWithBehavior(long itemId, String behaviorType) { // 这个实现需要遍历所有用户，实际应用中可能需要其他方式优化 // 这里仅作示例，实际项目应考虑性能影响 int userCount = 0; // 假设用户ID范围是1-10000 for (long userId = 1; userId <= 10000; userId++) { if (hasBehavior(userId, itemId, behaviorType)) { userCount++; } } return userCount; } /** * 计算用户之间的行为相似度（用于协同过滤推荐） * @return 返回两个用户共同行为的物品数量 */ public long calculateUserSimilarity(long userId1, long userId2, String behaviorType) { String key1 = getBehaviorKey(userId1, behaviorType); String key2 = getBehaviorKey(userId2, behaviorType); String destKey = "temp:similarity:" + userId1 + ":" + userId2 + ":" + behaviorType; // 使用AND操作找出共同行为 jedis.bitop("AND", destKey, key1, key2); long similarity = jedis.bitcount(destKey); // 清理临时键 jedis.del(destKey); return similarity; } /** * 基于用户行为生成物品推荐 * @return 推荐物品ID列表 */ public List<Long> getRecommendations(long userId, int limit) { List<Long> recommendations = new ArrayList<>(); Set<Long> alreadyViewed = new HashSet<>(); // 获取用户已浏览物品 String viewKey = getBehaviorKey(userId, VIEW); for (long i = 0; i < 10000; i++) { // 假设物品ID范围 if (jedis.getbit(viewKey, i)) { alreadyViewed.add(i); } } // 找出具有相似行为的用户 List<Long> similarUsers = findSimilarUsers(userId); // 从相似用户的浏览记录中推荐物品 for (Long similarUserId : similarUsers) { String otherViewKey = getBehaviorKey(similarUserId, VIEW); for (long i = 0; i < 10000; i++) { // 假设物品ID范围 if (recommendations.size() >= limit) { break; } // 只推荐用户未浏览过的物品 if (jedis.getbit(otherViewKey, i) && !alreadyViewed.contains(i)) { recommendations.add(i); alreadyViewed.add(i); // 避免重复推荐 } } } return recommendations; } // 查找相似用户 private List<Long> findSimilarUsers(long userId) { // 实际应用中可能需要更复杂的算法 // 这里仅作示例 List<Long> similarUsers = new ArrayList<>(); // 假设用户ID范围是1-10000 for (long otherUserId = 1; otherUserId <= 10000; otherUserId++) { if (userId == otherUserId) continue; long similarityScore = calculateUserSimilarity(userId, otherUserId, VIEW); if (similarityScore > 5) { // 相似度阈值 similarUsers.add(otherUserId); } if (similarUsers.size() >= 10) { break; // 限制相似用户数量 } } return similarUsers; } // 获取行为Key private String getBehaviorKey(long userId, String behaviorType) { return "user:" + userId + ":" + behaviorType; } } 应用场景5：IP地址统计与黑名单系统 场景描述 在网络安全和流量分析场景中，需要统计访问IP地址、识别异常IP、实现IP黑白名单功能。传统方案可能使用Hash或Set存储IP地址，但在大规模场景下内存消耗巨大。 BitMap解决方案 利用BitMap可以将IP地址映射为位偏移量，极大节省内存。IPv4地址共有2^32个（约43亿），使用BitMap只需512MB内存即可表示所有可能的IP地址。 实现示例 import redis.clients.jedis.Jedis; import java.net.InetAddress; import java.net.UnknownHostException; public class IPAddressTracker { private Jedis jedis; public IPAddressTracker(String host, int port) { this.jedis = new Jedis(host, port); } /** * 将IP地址添加到黑名单 */ public void addToBlacklist(String ipAddress) { long ipValue = ipToLong(ipAddress); jedis.setbit("ip:blacklist", ipValue, true); } /** * 检查IP是否在黑名单中 */ public boolean isBlacklisted(String ipAddress) { long ipValue = ipToLong(ipAddress); return jedis.getbit("ip:blacklist", ipValue); } /** * 记录IP访问 */ public void trackIPVisit(String ipAddress) { long ipValue = ipToLong(ipAddress); jedis.setbit("ip:visited", ipValue, true); } /** * 获取不同IP访问总数 */ public long getUniqueIPCount() { return jedis.bitcount("ip:visited"); } /** * 记录特定日期的IP访问 */ public void trackIPVisitByDate(String ipAddress, String date) { long ipValue = ipToLong(ipAddress); jedis.setbit("ip:visited:" + date, ipValue, true); } /** * 获取特定日期的不同IP访问数 */ public long getUniqueIPCountByDate(String date) { return jedis.bitcount("ip:visited:" + date); } /** * 获取连续多天都活跃的IP数量 */ public long getActiveIPsForDays(String[] dates) { if (dates.length == 0) return 0; String destKey = "temp:active:ips"; // 复制第一天的数据 jedis.bitop("AND", destKey, "ip:visited:" + dates[0]); // 对所有日期执行AND操作 for (int i = 1; i < dates.length; i++) { jedis.bitop("AND", destKey, destKey, "ip:visited:" + dates[i]); } long count = jedis.bitcount(destKey); jedis.del(destKey); return count; } /** * IP地址转为长整型 */ private long ipToLong(String ipAddress) { try { byte[] bytes = InetAddress.getByName(ipAddress).getAddress(); long result = 0; for (byte b : bytes) { result = result << 8 | (b & 0xFF); } return result; } catch (UnknownHostException e) { throw new IllegalArgumentException("Invalid IP address: " + ipAddress, e); } } /** * 长整型转为IP地址 */ private String longToIp(long ip) { return ((ip >> 24) & 0xFF) + "." + ((ip >> 16) & 0xFF) + "." + ((ip >> 8) & 0xFF) + "." + (ip & 0xFF); } } 应用实例：DDOS攻击防护 public class DDOSProtection { private IPAddressTracker ipTracker; private Jedis jedis; private String currentDateKey; public DDOSProtection(String host, int port) { this.jedis = new Jedis(host, port); this.ipTracker = new IPAddressTracker(host, port); updateDateKey(); } // 更新日期Key private void updateDateKey() { String date = java.time.LocalDate.now().toString(); this.currentDateKey = "ip:access:count:" + date; } /** * 记录IP访问并检查是否超过阈值 * @return true表示IP应被阻止 */ public boolean shouldBlockIP(String ipAddress, int accessLimit) { // 先检查是否已在黑名单 if (ipTracker.isBlacklisted(ipAddress)) { return true; } // 记录访问 long ipValue = ipToLong(ipAddress); String accessKey = currentDateKey + ":" + ipAddress; // 记录访问次数并检查 long accessCount = jedis.incr(accessKey); // 设置24小时过期 if (accessCount == 1) { jedis.expire(accessKey, 86400); } // 检查是否超过访问限制 if (accessCount > accessLimit) { // 添加到黑名单 ipTracker.addToBlacklist(ipAddress); return true; } return false; } /** * IP地址转为长整型 */ private long ipToLong(String ipAddress) { try { byte[] bytes = java.net.InetAddress.getByName(ipAddress).getAddress(); long result = 0; for (byte b : bytes) { result = result << 8 | (b & 0xFF); } return result; } catch (java.net.UnknownHostException e) { throw new IllegalArgumentException("Invalid IP address: " + ipAddress, e); } } } 性能优化与最佳实践 BitMap在Redis中高效强大，但使用时需注意以下几点 内存占用 精确计算 : 每8个bit占用1个字节，2^32位需要512MB 自动扩展 : Redis会根据设置的最大位偏移量自动扩展字符串 稀疏位图优化 : 对于非常稀疏的情况，可以考虑使用Hash结构代替 操作效率 单点操作 : GETBIT/SETBIT的时间复杂度为O(1) 范围操作 : BITCOUNT/BITPOS在大范围时消耗较大，可以限定范围 位运算 : BITOP的性能与操作数长度成正比，应避免对超大BitMap执行位运算 使用限制 偏移量上限 : 最大支持2^32-1的偏移量 原子性保证 : 所有位操作都是原子的，适合并发场景 持久化考虑 : 大量BitMap操作会增加AOF文件大小和RDB快照时间 最佳实践 合理设计键名 : 使用一致的命名规则，便于管理 定期清理 : 为临时BitMap设置过期时间 批量操作 : 使用BITFIELD命令批量处理位操作 缓存结果 : 对于频繁计算的位统计结果，可以缓存 监控内存 : 大量BitMap可能导致内存激增，应监控内存使用