You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

快速为树莓派监控摄像头Python MJPG流代码添加密码验证的方法

树莓派监控流密码校验实现方案

你可以直接使用HTTP Basic认证实现需求,仅需对原有代码做少量调整,无需引入数据库或Flask框架,密码直接硬编码在脚本中即可,同时会拦截未授权用户直接访问stream.mjpg地址绕过校验。

修改后完整代码

import io
import picamera
import logging
import socketserver
import base64
from threading import Condition
from http import server

# 硬编码认证凭证,可自行修改用户名和密码
USERNAME = "admin"
PASSWORD = "your_custom_password"

PAGE="""\
<html>
<head>
<title>Raspberry Pi - Surveillance Camera</title>
</head>
<body>
<center><h1>Raspberry Pi - Surveillance Camera</h1></center>
<center><img src="stream.mjpg" width="640" height="480"></center>
</body>
</html>
"""

class StreamingOutput(object):
    def __init__(self):
        self.frame = None
        self.buffer = io.BytesIO()
        self.condition = Condition()

    def write(self, buf):
        if buf.startswith(b'\xff\xd8'):
            # 新帧生成,复制缓存内容并通知所有客户端
            self.buffer.truncate()
            with self.condition:
                self.frame = self.buffer.getvalue()
                self.condition.notify_all()
            self.buffer.seek(0)
        return self.buffer.write(buf)

class StreamingHandler(server.BaseHTTPRequestHandler):
    def do_GET(self):
        # 优先校验认证信息
        auth_header = self.headers.get('Authorization')
        authenticated = False
        if auth_header and auth_header.startswith('Basic '):
            try:
                auth_decoded = base64.b64decode(auth_header.split(' ')[1]).decode('utf-8')
                req_user, req_pass = auth_decoded.split(':', 1)
                if req_user == USERNAME and req_pass == PASSWORD:
                    authenticated = True
            except Exception:
                pass
        
        if not authenticated:
            self.send_response(401)
            self.send_header('WWW-Authenticate', 'Basic realm="Raspberry Pi Camera"')
            self.send_header('Content-Type', 'text/html')
            self.end_headers()
            self.wfile.write(b'请输入正确的用户名和密码访问监控')
            return

        # 原有业务逻辑保持不变
        if self.path == '/':
            self.send_response(301)
            self.send_header('Location', '/index.html')
            self.end_headers()
        elif self.path == '/index.html':
            content = PAGE.encode('utf-8')
            self.send_response(200)
            self.send_header('Content-Type', 'text/html')
            self.send_header('Content-Length', len(content))
            self.end_headers()
            self.wfile.write(content)
        elif self.path == '/stream.mjpg':
            self.send_response(200)
            self.send_header('Age', 0)
            self.send_header('Cache-Control', 'no-cache, private')
            self.send_header('Pragma', 'no-cache')
            self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME')
            self.end_headers()
            try:
                while True:
                    with output.condition:
                        output.condition.wait()
                        frame = output.frame
                    self.wfile.write(b'--FRAME\r\n')
                    self.send_header('Content-Type', 'image/jpeg')
                    self.send_header('Content-Length', len(frame))
                    self.end_headers()
                    self.wfile.write(frame)
                    self.wfile.write(b'\r\n')
            except Exception as e:
                logging.warning(
                    'Removed streaming client %s: %s',
                    self.client_address, str(e))
        else:
            self.send_error(404)
            self.end_headers()

class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
    allow_reuse_address = True
    daemon_threads = True

with picamera.PiCamera(resolution='640x480', framerate=24) as camera:
    output = StreamingOutput()
    # 取消下一行注释修改摄像头旋转角度(单位:度)
    #camera.rotation = 90
    camera.start_recording(output, format='mjpeg')
    try:
        address = ('', 8000)
        server = StreamingServer(address, StreamingHandler)
        server.serve_forever()
    finally:
        camera.stop_recording()

自定义页面内密码输入框方案(可选)

如果需要在HTML页面内嵌入密码输入框而非使用浏览器默认弹框,可以调整PAGE模板增加表单提交逻辑,配合Cookie实现校验,仅需额外修改以下部分:

  1. 将PAGE修改为包含密码输入框的登录页,未校验通过时返回登录页,校验通过后写入Cookie
  2. 增加do_POST方法处理密码提交请求
  3. 所有请求优先校验Cookie有效性

内容的提问来源于stack exchange,提问作者alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 04:42:03