使用HTTPS 服务在浏览器端使用摄像头的方式解析

1.方式1

bash 复制代码
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import basicSsl from '@vitejs/plugin-basic-ssl'

export default defineConfig({
  plugins: [
    vue(),
    basicSsl({
      name: 'test',
      domains: ['192.168.15.166', 'localhost'], // 添加您的IP
      certDir: './cert',
    }),
  ],
  server: {
    host: '0.0.0.0',
    port: 3000,
    https: true,
    proxy: {
      '/api': {
        target: 'http://localhost:5000',
        changeOrigin: true,
      },
    },
  },
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    sourcemap: false,
    minify: 'terser',
    rollupOptions: {
      output: {
        chunkFileNames: 'js/[name]-[hash].js',
        entryFileNames: 'js/[name]-[hash].js',
        assetFileNames: '[ext]/[name]-[hash].[ext]',
      },
    },
  },
})

2.方式2

bash 复制代码
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import fs from 'fs'
import path from 'path'


export default defineConfig({
  plugins: [
    vue(),
  ],
  server: {
    https: {
       key: fs.readFileSync(path.resolve(__dirname, 'cert/key.pem')),
       cert: fs.readFileSync(path.resolve(__dirname, 'cert/cert.pem'))
     },
    host: '0.0.0.0',
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:5000',
        changeOrigin: true
      },
    },
  },
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    sourcemap: false,
    minify: 'terser',
  },
})

这两个 Vite 配置文件都实现了 HTTPS 服务,这是浏览器访问摄像头等敏感设备的必要条件。以下详细解释它们的原理和差异。

为什么需要 HTTPS

现代浏览器出于安全考虑,要求在以下情况下才能访问摄像头、麦克风等敏感设备:

  • 使用 HTTPS 协议(安全连接)
  • 或者是 localhost 域名(本地开发特例)

这是因为 getUserMedia() API 被归类为"强大功能"(Powerful Features),需要安全上下文(Secure Context)。

方式一:使用 @vitejs/plugin-basic-ssl 插件

javascript 复制代码
plugins: [
  vue(),
  basicSsl({
    name: 'test',
    domains: ['192.168.31.179', 'localhost'],
    certDir: './cert',
  }),
]

原理:

  1. 自动生成证书:插件会自动为指定的域名生成自签名 SSL 证书
  2. 证书存储 :生成的证书存储在 ./cert 目录中
  3. 多域名支持:可以为多个域名/IP 生成证书(如局域网 IP 和 localhost)
  4. 自动配置:插件会自动将证书配置到 Vite 的 HTTPS 服务器

工作流程:

  • 首次运行时,插件检查证书是否存在
  • 如果不存在,使用内置的证书生成器创建自签名证书
  • 将证书自动注入到 Vite 的 server 配置中
  • 后续运行时复用已生成的证书

方式二:手动配置证书

javascript 复制代码
server: {
  https: {
    key: fs.readFileSync(path.resolve(__dirname, 'cert/key.pem')),
    cert: fs.readFileSync(path.resolve(__dirname, 'cert/cert.pem'))
  },
}

原理:

  1. 手动管理证书:需要预先生成或获取 SSL 证书文件
  2. 文件读取 :使用 Node.js 的 fs 模块直接读取证书文件
  3. 直接配置:将证书内容直接传递给 Vite 的 HTTPS 配置

证书生成方式(通常使用 OpenSSL):

bash 复制代码
# 生成私钥
openssl genrsa -out key.pem 2048
# 生成证书
openssl req -new -x509 -key key.pem -out cert.pem -days 365

关键差异对比

特性 方式一(plugin-basic-ssl) 方式二(手动配置)
便利性 高 - 自动生成和管理 低 - 需要手动创建证书
灵活性 中等 - 插件预设配置 高 - 完全自定义
证书类型 自签名证书 可以使用任何证书(自签名或CA签发)
多域名支持 内置支持,配置简单 需要生成包含 SAN 的证书
首次设置 零配置,自动完成 需要手动生成证书
团队协作 每个开发者自动生成 需要共享证书文件
证书更新 删除旧证书即可重新生成 手动重新生成和替换
依赖性 需要安装额外插件 仅需 Node.js 内置模块

实际应用场景

方式一适合:

  • 本地开发环境
  • 快速原型开发
  • 需要在局域网内多设备测试
  • 团队成员各自开发,不共享证书

方式二适合:

  • 需要使用特定证书(如公司内部 CA 签发)
  • 生产环境的本地模拟
  • 需要精确控制证书配置
  • 已有证书管理流程的团队

浏览器访问摄像头的实现

无论使用哪种方式,配置 HTTPS 后都可以使用以下代码访问摄像头:

javascript 复制代码
navigator.mediaDevices.getUserMedia({ 
  video: true, 
  audio: true 
})
.then(stream => {
  // 获取到媒体流
  videoElement.srcObject = stream;
})
.catch(err => {
  console.error('无法访问摄像头:', err);
});

注意事项

  1. 证书信任:两种方式生成的自签名证书都会导致浏览器警告,需要手动信任
  2. 局域网访问:方式一通过配置 IP 地址,可以让局域网内其他设备通过 HTTPS 访问
  3. 安全性:这些配置仅适用于开发环境,生产环境应使用正规 CA 签发的证书

总的来说,方式一更适合快速开发和原型验证,方式二则提供了更多的控制权和灵活性。选择哪种方式主要取决于项目需求和团队的技术偏好。

这是使用自签名证书进行本地开发时的常见问题。让我详细说明解决方案:

一、手机无法访问的问题排查

1. 确保网络连通性

javascript 复制代码
// vite.config.js
server: {
  host: '0.0.0.0',  // 允许外部访问
  port: 3000,
  https: true,
}

检查步骤:

  • 确保手机和电脑在同一局域网
  • 电脑防火墙允许 3000 端口
  • 使用电脑的局域网 IP(如 https://192.168.31.179:3000

2. 防火墙设置

bash 复制代码
# Windows - 以管理员运行
netsh advfirewall firewall add rule name="Vite Dev Server" dir=in action=allow protocol=TCP localport=3000

# macOS
sudo pfctl -d  # 临时关闭防火墙测试

# Linux
sudo ufw allow 3000/tcp

二、手机端安装证书的正确方法

iOS 设备

  1. 生成包含正确信息的证书:
bash 复制代码
# 创建证书配置文件 cert.conf
cat > cert.conf <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
C = CN
ST = State
L = City
O = Organization
CN = 192.168.31.179

[v3_req]
keyUsage = critical, digitalSignature, keyAgreement
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost
IP.1 = 192.168.31.179
IP.2 = 127.0.0.1
EOF

# 生成证书
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout key.pem -out cert.pem -config cert.conf
  1. 安装证书到 iOS:
  • cert.pem 文件通过邮件/AirDrop 发送到手机
  • 点击证书文件,选择"安装"
  • 设置 → 通用 → 关于本机 → 证书信任设置
  • 开启对该证书的完全信任

Android 设备

  1. Android 安装步骤:
  • cert.pem 重命名为 cert.crt
  • 传输到手机
  • 设置 → 安全 → 加密与凭据 → 安装证书 → CA 证书
  • 选择证书文件安装

三、使用 mkcert 工具(推荐方案)

mkcert 可以创建本地受信任的证书,避免所有警告:

安装 mkcert

bash 复制代码
# Windows (使用 Chocolatey)
choco install mkcert

# macOS
brew install mkcert

# Linux
apt install libnss3-tools
wget -O mkcert https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64
chmod +x mkcert
sudo mv mkcert /usr/local/bin/

配置本地 CA 并生成证书

bash 复制代码
# 1. 安装本地 CA
mkcert -install

# 2. 生成证书(包含所有需要的域名和 IP)
mkcert -cert-file cert.pem -key-file key.pem localhost 127.0.0.1 192.168.31.179 ::1

# 3. 为手机生成 CA 证书
mkcert -CAROOT  # 显示 CA 证书位置

更新 Vite 配置

javascript 复制代码
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import fs from 'fs'
import path from 'path'

export default defineConfig({
  plugins: [vue()],
  server: {
    https: {
      key: fs.readFileSync('./key.pem'),
      cert: fs.readFileSync('./cert.pem')
    },
    host: '0.0.0.0',
    port: 3000,
  }
})

手机端信任 mkcert CA

  1. 获取 CA 证书:
bash 复制代码
# 找到 CA 证书位置
mkcert -CAROOT
# 通常在: 
# Windows: %LOCALAPPDATA%\mkcert
# macOS: ~/Library/Application Support/mkcert
# Linux: ~/.local/share/mkcert
  1. rootCA.pem 发送到手机并安装

四、使用 ngrok(外网访问方案)

如果证书问题难以解决,可以使用 ngrok 提供的 HTTPS 隧道:

bash 复制代码
# 安装 ngrok
npm install -g ngrok

# 启动本地服务后,创建隧道
ngrok http 3000

# 会得到类似这样的地址:
# https://abc123.ngrok.io

优点:

  • 自动 HTTPS,无需证书配置
  • 可以外网访问
  • 手机无需安装证书

五、开发环境最佳实践配置##

javascript 复制代码
// vite.config.js - 完整的 HTTPS 配置方案
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import fs from 'fs'
import path from 'path'
import { execSync } from 'child_process'

// 获取本机局域网 IP
function getLocalIP() {
  const os = require('os')
  const interfaces = os.networkInterfaces()
  for (const name of Object.keys(interfaces)) {
    for (const iface of interfaces[name]) {
      if (iface.family === 'IPv4' && !iface.internal) {
        return iface.address
      }
    }
  }
  return '127.0.0.1'
}

// 自动生成证书函数
function ensureCertificates() {
  const certDir = path.resolve(__dirname, 'cert')
  const keyPath = path.join(certDir, 'key.pem')
  const certPath = path.join(certDir, 'cert.pem')
  
  // 检查证书是否存在
  if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
    console.log('✅ 使用现有证书')
    return { key: keyPath, cert: certPath }
  }
  
  // 创建证书目录
  if (!fs.existsSync(certDir)) {
    fs.mkdirSync(certDir, { recursive: true })
  }
  
  const localIP = getLocalIP()
  console.log(`📱 局域网 IP: ${localIP}`)
  
  // 检查是否安装了 mkcert
  try {
    execSync('mkcert -version', { stdio: 'ignore' })
    console.log('🔧 使用 mkcert 生成受信任的证书...')
    
    // 安装本地 CA(如果还没安装)
    execSync('mkcert -install', { stdio: 'inherit' })
    
    // 生成证书
    execSync(
      `mkcert -cert-file ${certPath} -key-file ${keyPath} localhost 127.0.0.1 ${localIP} ::1`,
      { stdio: 'inherit' }
    )
    
    console.log('✅ mkcert 证书生成成功!')
    console.log('📱 手机端请安装 CA 证书:')
    const caRoot = execSync('mkcert -CAROOT').toString().trim()
    console.log(`   ${path.join(caRoot, 'rootCA.pem')}`)
    
  } catch (e) {
    console.log('⚠️  mkcert 未安装,使用 OpenSSL 生成自签名证书...')
    
    // 创建 OpenSSL 配置
    const configPath = path.join(certDir, 'openssl.cnf')
    const opensslConfig = `
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
C = CN
ST = State
L = City
O = Dev Organization
CN = ${localIP}

[v3_req]
keyUsage = critical, digitalSignature, keyAgreement
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost
DNS.2 = *.localhost
IP.1 = 127.0.0.1
IP.2 = ${localIP}
IP.3 = ::1
`
    fs.writeFileSync(configPath, opensslConfig)
    
    // 生成证书
    try {
      execSync(
        `openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ${keyPath} -out ${certPath} -config ${configPath}`,
        { stdio: 'inherit' }
      )
      console.log('✅ 自签名证书生成成功!')
      console.log('⚠️  浏览器会显示安全警告,需要手动信任证书')
    } catch (error) {
      console.error('❌ 证书生成失败:', error)
      process.exit(1)
    }
  }
  
  return { key: keyPath, cert: certPath }
}

// 生成或获取证书
const certificates = ensureCertificates()

export default defineConfig({
  plugins: [vue()],
  
  server: {
    https: {
      key: fs.readFileSync(certificates.key),
      cert: fs.readFileSync(certificates.cert)
    },
    host: '0.0.0.0',  // 允许外部访问
    port: 3000,
    
    // 显示二维码方便手机扫描
    open: false,
    
    // 配置 CORS
    cors: true,
    
    // 代理配置
    proxy: {
      '/api': {
        target: 'http://localhost:5000',
        changeOrigin: true,
        secure: false,
      }
    }
  },
  
  // 构建配置
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    sourcemap: false,
    minify: 'terser',
    rollupOptions: {
      output: {
        chunkFileNames: 'js/[name]-[hash].js',
        entryFileNames: 'js/[name]-[hash].js',
        assetFileNames: '[ext]/[name]-[hash].[ext]',
      }
    }
  },
  
  // 优化配置
  optimizeDeps: {
    exclude: ['vue']
  }
})

// 启动后打印访问信息
process.nextTick(() => {
  const localIP = getLocalIP()
  console.log('\n🚀 服务已启动:')
  console.log(`   本地: https://localhost:3000`)
  console.log(`   局域网: https://${localIP}:3000`)
  console.log('\n📱 手机访问提示:')
  console.log('   1. 确保手机与电脑在同一 WiFi')
  console.log('   2. 如有证书警告,点击"高级"→"继续访问"')
  console.log('   3. iOS 需在"设置"→"通用"→"关于本机"→"证书信任设置"中信任证书\n')
})

六、配套的 package.json 脚本

javascript 复制代码
{
  "scripts": {
    "dev": "vite",
    "dev:host": "vite --host",
    "dev:network": "vite --host 0.0.0.0",
    "dev:https": "node scripts/setup-https.js && vite",
    "cert:install": "mkcert -install",
    "cert:generate": "mkcert -cert-file cert/cert.pem -key-file cert/key.pem localhost 127.0.0.1 $(ipconfig getifaddr en0 || hostname -I | awk '{print $1}') ::1",
    "cert:trust": "mkcert -install && npm run cert:generate",
    "build": "vite build",
    "preview": "vite preview",
    "tunnel": "ngrok http 3000"
  },
  "devDependencies": {
    "@vitejs/plugin-basic-ssl": "^1.0.1",
    "@vitejs/plugin-vue": "^4.2.3",
    "vite": "^4.3.9",
    "vue": "^3.3.4"
  },
  "dependencies": {
    "qrcode": "^1.5.3"
  }
}

七、快速解决方案总结

最简单的方案(推荐)

  1. 安装 mkcert
bash 复制代码
# 一次性设置,永久解决证书问题
brew install mkcert  # macOS
mkcert -install
  1. 生成证书
bash 复制代码
mkcert -cert-file cert.pem -key-file key.pem localhost 192.168.xxx.xxx
  1. 手机端信任
  • ~/.local/share/mkcert/rootCA.pem(或对应系统路径)发送到手机
  • 安装为受信任的 CA 证书

故障排查清单

网络连接

  • 手机和电脑同一 WiFi?
  • 防火墙允许 3000 端口?
  • 使用正确的 IP 地址?

证书配置

  • 证书包含局域网 IP?
  • 证书已安装到手机?
  • iOS 已在"证书信任设置"中启用?

Vite 配置

  • host: '0.0.0.0' 已设置?
  • https: true 或手动配置证书?
  • 端口没有被占用?

终极备用方案

如果以上都不行,使用 localtunnelngrok

bash 复制代码
npx localtunnel --port 3000 --subdomain myapp
# 或
ngrok http 3000

这样可以获得一个临时的公网 HTTPS 地址,无需任何证书配置,手机可以直接访问。

主要缺点是需要依赖外部服务,并且速度可能较慢。但对于紧急测试和演示,这是最快的解决方案。

相关推荐
不会叫的狼2 小时前
HTTPS + 域名 + 双向证书认证(下)
https
疯狂的维修5 小时前
关于Gateway configration studio软件配置网关
网络协议·c#·自动化·gateway
wow_DG6 小时前
【WebSocket✨】入门之旅(五):WebSocket 的安全性
网络·websocket·网络协议
拷贝码农卡卡东16 小时前
pre-commit run --all-files 报错:http.client.RemoteDisconnected
网络·网络协议·http
DoWhatUWant16 小时前
域格YM310 X09移芯CAT1模组HTTPS连接服务器
服务器·网络协议·https
又菜又爱玩呜呜呜~19 小时前
go使用反射获取http.Request参数到结构体
开发语言·http·golang
言之。20 小时前
TCP 拥塞控制设计空间课程要点总结
网络·网络协议·tcp/ip
cellurw21 小时前
Linux下C语言实现HTTP+SQLite3电子元器件查询系统
linux·c语言·http