Python HTTP服务安全认证:Basic Auth三种实现方案

发布时间:2026/10/29 3:59:07

Python HTTP服务安全认证:Basic Auth三种实现方案 1. 先搞清楚我们到底在给什么加认证如果你自己搭过 Python 的临时 HTTP 服务一定见过这条命令python -m http.server 8000它能把当前目录变成一个可浏览下载的网站局域网里打开http://192.168.x.x:8000就能访问。问题也出在这里这个服务默认不设防只要有人扫到端口目录里所有文件都能被拉走。之前我用它给别人发资料结果没过多久就在日志里看到一堆来自陌生地址的探测请求才意识到内网也不是绝对安全。所以我养成了一个习惯不管多临时只要服务要开放给不是自己一个人用就先加上最简单的用户名密码认证。认证方式不需要复杂HTTP 协议自带的 Basic Auth 就够用。浏览器会自己弹登录框用户不需要记住任何额外链接也不需要配置客户端比“在 URL 里塞 token”这种野路子正规得多。这篇文章我会用三种方案实现同一件事基于标准库http.server的SimpleHTTPRequestHandler加认证适合静态文件共享基于BaseHTTPRequestHandler自定义请求处理适合你要自己写路由的 API 服务基于 WSGI 中间件配合waitress部署适合你打算把服务跑稳一点、以后要接正式环境的情况。你看完以后按自己的场景选一种抄走就行不用三份都读。我会把代码里的每个关键判断都解释清楚顺便把我踩过的坑一并说掉。1.1 默认的 http.server 为什么不行http.server是 Python 标准库内置的模块很多入门教程都拿它演示“一行代码开网站”。它底层调用socketserver默认监听0.0.0.0也就是说它会绑定本机所有网卡地址。在物理服务器上这等于所有能访问到这个主机的用户都能看到服务在个人电脑上哪怕你只连了公司局域网也会被同一个 WiFi 下的人扫到。最直接的问题是没有认证。SimpleHTTPRequestHandler只会忠实地把磁盘文件映射成 URL任何人发起 GET 请求都能拿到内容。它对HEAD、GET、POST的处理也很简单HEAD只返回头信息GET返回文件内容POST默认直接报 501。这种设计本来就不是给生产环境用的它连基本的访问控制接口都没暴露出来。并不是说它没用而是说你应该在它外面加一层“门禁”。加认证的本质是在请求真正进入目录映射逻辑之前先检查Authorization请求头里的凭据是否正确。只要校验没通过直接返回 401并告诉浏览器去弹登录框。1.2 Basic Auth 的原理和适用范围Basic Auth 是 HTTP/1.0 时代就定下的认证方式。它的流程非常简单客户端第一次请求时通常没有Authorization头服务端发现没有这个头返回401 Unauthorized并在响应头里带上WWW-Authenticate: Basic realmxxx浏览器的登录框读取 realm 作为提示文字用户输入用户名密码浏览器用冒号把用户名和密码拼成username:password再做一次 Base64 编码放到请求头里发回来服务端解出这一段比对用户名密码通过就继续处理请求不通过就再次返回 401。这里要强调一点Base64 不是加密它只是一种可逆的编码形式。把admin:123456编码成一串字符随便找个在线工具就能倒推回来。所以 Basic Auth 只在局域网、内网或者有 HTTPS 加密的前提下算“能用”。它在公网上裸奔等于把密码明文交给网络里的任何中间设备。1.3 三种方案的适用场景对比我先把三种方案的适用场景列成表格方便你快速定位。方案依赖适合场景代码量维护成本方案一SimpleHTTPRequestHandler标准库共享静态文件、临时下载页少低方案二BaseHTTPRequestHandler标准库自写 API、自定义路由、网关式服务中中方案三WSGI waitresswaitress、werkzeug正式部署、多线程、后续要接 WSGI 应用中中如果只是给前端打包出来的页面做本地预览方案一足够如果是给用户提供一个带登录态的接口服务方案二更好如果你已经习惯了 Flask 那种app.route的写法又不想被框架绑定方案三可以让你用纯 WSGI 方式搭服务后面换成 Gunicorn 也没压力。2. 方案一给静态文件服务器套上认证方案一改造的是http.server里最常用的SimpleHTTPRequestHandler不需要引入任何第三方库适合python -m http.server这种一键启动场景。我实际的使用场景一般是临时把某个目录开放给同事下载资料、给项目组共享构建产物、或者在自己电脑上预览一个静态站点。这类需求最看重的就是“改得少、跑得快”。2.1 手写 Authorization 校验函数我们需要一个函数从请求头里取出Authorization判断它是不是 Basic Auth并把 Base64 解码后的用户名密码拆出来比对。import base64 USERNAME admin PASSWORD secret123 def is_authorized(headers): auth headers.get(Authorization, ) if not auth.startswith(Basic ): return False try: payload base64.b64decode(auth[6:]).decode(utf-8) username, _, password payload.partition(:) except Exception: return False return username USERNAME and password PASSWORD我特意加了try/except因为你不能假设客户端每次发的都是合法 Base64。有人用脚本扫描时Authorization头可能是一些乱写的值比如Basic !或者直接把 Token 放在 Bearer 头里。一旦base64.b64decode解不出来我们的代码不能像没看见一样继续往下面走而是统一当作未认证处理。partition(:)返回一个三元组这里只需要用户名和密码。注意用户名里不能有冒号因为协议约定第一个冒号是分隔符密码里如果有冒号反而没事partition只按第一次出现的位置拆分。2.2 用 send_head 统一拦截所有请求接下来新建一个请求处理类继承SimpleHTTPRequestHandler并重写send_head。为什么要重写这个方法而不是do_GET因为SimpleHTTPRequestHandler的do_GET和do_HEAD最后都会调用send_head去构造响应所以在这里拦截等于一次性覆盖了 GET 和 HEAD 两种请求。from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer class AuthHandler(SimpleHTTPRequestHandler): def send_head(self): if not is_authorized(self.headers): self.send_response(401) self.send_header( WWW-Authenticate, Basic realmprivate-files ) self.send_header(Content-Length, 0) self.end_headers() return None return super().send_head()如果is_authorized返回 False我们直接返回 401 和一个WWW-Authenticate头。realm是登录框上显示的提示信息浏览器会根据它缓存同一套凭据。如果你只有一个服务这个值写什么都行如果你在同一站点可能部署多个独立认证区域realm 要小心区分否则浏览器会混用凭据。返回None是合法的因为do_GET拿到send_head的返回值之后只有返回值是真值时才继续写文件内容否则就直接结束连接。写一个空 body 的 401 响应既不会让恶意请求拿走任何文件也不会浪费带宽。最后加上启动入口def main(): server ThreadingHTTPServer((0.0.0.0, 8000), AuthHandler) print(serve on 0.0.0.0:8000) server.serve_forever() if __name__ __main__: main()我用ThreadingHTTPServer而不是HTTPServer是因为它每来一个连接就开一个线程处理遇到多个浏览器同时下载文件时不会互相阻塞。Python 3.7 之后都内置了这个类不需要额外装线程库。如果你只是在自己电脑上临时跑一下用哪个区别不大。2.3 用命令行参数动态配置用户名密码把用户名密码写死在代码里有个问题换一次密码就要改代码。对临时服务来说这其实挺烦的。所以我会在启动脚本里加上argparseimport argparse parser argparse.ArgumentParser() parser.add_argument(--username, defaultadmin) parser.add_argument(--password, defaultsecret123) parser.add_argument(--port, typeint, default8000) parser.add_argument(--directory, default.) args parser.parse_args()启动时就可以这样用python auth_server.py --username alice --password pss --port 9000 --directory ./share这里有个容易踩的坑千万不要把密码直接写在命令行历史里尤其当你在共享机器上操作时。临时用用可以正式一点的话把密码放到环境变量或者本地配置文件里脚本里读取os.environ的值这样命令历史里就不会留下明文密码。用环境变量还有个好处后面配合 Docker 或 systemd 部署时不用改代码只改环境变量就能换密码。3. 方案二BaseHTTPRequestHandler 打造自定义 API 认证如果你不只是想共享文件而是想提供一个带认证的 JSON 接口那么SimpleHTTPRequestHandler就不够灵活了。它把目录映射逻辑写死了你想加一个/api/status路由、想区分 GET 和 POST、想返回 JSON 而不是文件内容都得费劲绕过去。这时候应该直接继承BaseHTTPRequestHandler把请求处理的逻辑握在自己手里。3.1 一个带路由的最小认证服务器继承BaseHTTPRequestHandler之后你需要自己实现do_GET、do_POST等方法。认证检查可以写成一个私有方法在所有入口统一调用。下面是去掉文件服务、只返回 JSON 的最小示例import base64 import json from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer USERNAME alice PASSWORD pssw0rd class AuthAPIHandler(BaseHTTPRequestHandler): def _check_auth(self): auth self.headers.get(Authorization, ) if not auth.startswith(Basic ): return False try: raw base64.b64decode(auth[6:]).decode(utf-8) user, _, pwd raw.partition(:) except Exception: return False return user USERNAME and pwd PASSWORD def _send_401(self): self.send_response(401) self.send_header(WWW-Authenticate, Basic realmapi) self.send_header(Content-Type, application/json; charsetutf-8) body json.dumps({error: authentication required}).encode(utf-8) self.send_header(Content-Length, str(len(body))) self.end_headers() self.wfile.write(body) def _send_json(self, data, status200): body json.dumps(data).encode(utf-8) self.send_response(status) self.send_header(Content-Type, application/json; charsetutf-8) self.send_header(Content-Length, str(len(body))) self.end_headers() self.wfile.write(body) def do_GET(self): if not self._check_auth(): self._send_401() return if self.path /api/ping: self._send_json({message: pong}) elif self.path /api/time: self._send_json({now: 2025-01-01T00:00:00}) else: self._send_json({error: not found}, status404) def log_message(self, fmt, *args): print(f{self.address_string()} - {fmt % args})注意我在 401 响应里设置了Content-Length为 JSON body 的长度而不是直接调send_error。send_error会返回 HTML 错误页对前后端分离的项目不太友好。如果你要做一个纯 JSON API最好统一用_send_json这种方式控制响应格式。前端拿到 401 后只要看到响应头里有WWW-Authenticate就能触发浏览器的登录弹窗。3.2 给接口加 POST 和错误处理很多入门项目只实现了do_GET结果前端用fetch发POST请求时直接得到 501。继承BaseHTTPRequestHandler后所有方法的默认实现都是返回 501所以如果你不实现do_POST它就会报“Unsupported method”。如果你要接收 JSON记得手动读取并解析 body。class AuthAPIHandler(BaseHTTPRequestHandler): def do_POST(self): if not self._check_auth(): self._send_401() return try: length int(self.headers.get(Content-Length, 0)) payload self.rfile.read(length) data json.loads(payload.decode(utf-8)) except Exception: self._send_json({error: bad json}, status400) return if self.path /api/submit: self._send_json({received: data}) else: self._send_json({error: not found}, status404)需要留意的是这里的Content-Length是用字符串形式接收的布尔判断时0是 True所以要用int()转换。另外self.rfile.read(length)若客户端没有正确的 Content-Length 头可能
延伸阅读

更多相关文章

2026/10/27 8:24:44

Python等值线图绘制实战:从插值到出图全流程

简介:这是一份基于D3.js实现等值线图、等高线图与等值云图的Web可视化示例,适合前端开发者、GIS爱好者及需要在地理或气象数据展示中应用插值算法的工程师参考。资源通过反距离加权法(IDW)对离散点数据进行空间插值,再…

2026/10/27 11:21:01

技能体系构建指南:从技能树到刻意练习的完整方法论

1. 简历上的Skills:一个被严重低估的词语1.1 从一次面试说起前几年我做技术面试官的时候,遇到过不少简历上Skills栏写得满满当当的候选人。从Python到Photoshop,从项目管理到视频剪辑,长长一排,乍一看是个全能选手。但…

2026/10/29 3:58:56

RISC-V中文手册深度解析:指令集设计哲学与硬件底层逻辑

简介:本资源是面向嵌入式开发、计算机体系结构学习者及RISC-V初学者的权威中文入门指南,系统讲解RISC-V指令集架构的设计哲学与工程实践。手册由David Patterson与Andrew Waterman原著,勾凌睿、黄成、刘志刚等译,包云岗校阅&#…

2026/10/29 3:58:56

Agent-Reach:轻量级CLI代理调度中枢

1. 项目概述:Agent-Reach 是什么,它解决的到底是什么问题?Agent-Reach 不是一个泛泛而谈的“智能体框架”或“AI工具集合”,而是一个面向开发者、运维工程师和自动化流程设计者的轻量级命令行代理调度中枢。它的核心定位非常明确&…

2026/10/29 3:58:56

马尾辫怎么扎好看?从高度、角度到发量蓬松的完整发型指南

很多人看到"ponytail"这个词,第一反应是"这有什么好讲的,不就是把头发扎起来吗"。但我在造型这行待得越久,越觉得马尾辫是看起来最简单、实际最考验手法的发型。同样随手一扎,有人扎完显得脸小、头型圆润、气…

2026/10/29 3:58:56

Apache SkyWalking全链路追踪实战与性能优化指南

1. 项目背景与核心价值在分布式系统架构成为主流的今天,服务之间的调用关系变得异常复杂。一个简单的用户请求可能涉及数十个微服务的协同工作,当出现性能瓶颈或异常时,传统的监控手段往往难以快速定位问题根源。这正是全链路追踪技术大显身手…

2026/10/29 3:58:56

RISC-V RV32I指令集深度解析:从编码格式到硬件执行语义

简介:RISC-V中文手册.pdf是一份面向嵌入式开发工程师、计算机体系结构学习者及开源硬件爱好者的权威入门与进阶指南,系统解决RISC-V指令集架构理解难、资料分散、中文优质资源稀缺等问题。手册由David Patterson与Andrew Waterman原著,勾凌睿…

2026/10/29 3:53:56

统信UOS系统APT命令详解与实战技巧

1. 统信UOS系统与APT命令概述统信UOS作为国产操作系统的代表,其底层基于Debian Linux发行版,继承了强大的APT包管理工具。对于系统管理员和开发者而言,熟练掌握APT命令是日常运维的基础技能。不同于Windows系统的图形化软件管理,U…

2026/10/28 14:42:12

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/28 15:28:56

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/28 3:32:29

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/29 0:03:42

学生选课系统DFD图绘制指南:边界划分与分层校验实战

简介:文档以顶层与第一层DFD图为核心,完整刻画学生选课系统的逻辑模型,面向软件工程课程设计、系统分析学习及毕设前期建模人群。顶层图界定系统边界,外部实体包括管理员、教师和学生三类角色;第一层图进一步细化用户登…

2026/10/29 0:03:42

JIRA敏捷落地:Scrum角色权限、Sprint与燃尽图避坑指南

简介:围绕“基于JIRA的敏捷开发项目管理”这一主题,面向产品负责人、Scrum Master及研发测试成员,梳理Scrum角色分工、迭代流程与JIRA工具配置的对应关系,适合初、中级团队导入敏捷管理时参考。压缩包共1个docx文件,大…

2026/10/29 0:03:42

SEMI S8人机工程验收实战:从条款拆解到现场实测的完整指南

简介:面向半导体设备设计、制造与运维工程师及安全合规人员,这份资料聚焦SEMI标准体系下的设备安全规范,重点收录SEMI S8-0308《半导体制造设备人体工程学工程安全指南》。内容围绕设备设计原则、错误与事故预防、人体工程学适配三大主线展开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑