豆干 HTTP 获取图片 1. 获取图片的过程 获取图片的流程如下图所示: 各个阶段对应的示例报文(使用 Wireshark 抓取): 总共包含 4 个报文(192.168.1.130 为客户端,192.168.1.60 为设备端): 1) 客户端发送获取图片请求 2) 设备端返回需要鉴权报文(其中 WWW-Authenticate 那一行指定了需要使用什么方式鉴权及鉴权相关信息) 3) 客户端完成鉴权,并再次发送获取图片请求(其中Authorization 那一行即是客户端完成鉴权后的加密信息) 4) 设备端返回图片 2. Postman 示例 2.1 发出获取图片请求,返回要求鉴权报文,从中获取鉴权所需信息 2.2 根据鉴权所需信息生成鉴权信息,添加到请求头的 Authorization 字段中,鉴权信息使用通用 HTTP 认证中的 Digest 模式生成,具体请参考下方 3.2 备注一节 3. 代码示例 3.1 Python 代码 #!/usr/bin/env python3 # -*- coding: utf-8 -*- import requests from hashlib import md5 from PIL import Image from io import BytesIO IP = "192.168.1.60" SNAPSHOT_URI = "/snapshot.jpg" SNAPSHOT_URL = "http://" + IP + SNAPSHOT_URI USER = "admin" PASSWD = "tp123456" s = requests.Session() res = s.get(SNAPSHOT_URL) # 尝试获取图片 if res.status_code == 401: # 需要鉴权 # 鉴权方式为 WWW-Authenticate Digest auth_info = res.headers["WWW-Authenticate"] token = auth_info.split('"') realm = token[1] nonce = token[3] user_info = USER + ":" + realm + ":" + PASSWD method = "GET:" + SNAPSHOT_URI response = md5( ( md5(user_info.encode(encoding="utf-8")).hexdigest() + ":" + nonce + ":" + md5(method.encode(encoding="utf-8")).hexdigest() ).encode(encoding="utf-8") ).hexdigest() authorization = ( "Digest username=\"" + USER + "\",realm=\"" + realm + "\",nonce=\"" + nonce + "\",uri=\"" + SNAPSHOT_URI + "\",response=\"" + response + "\"" ) s.headers["Authorization"] = authorization # 设置鉴权信息 res = s.get(SNAPSHOT_URL) # 再次获取图片 # 保存图片到本地 im = Image.open(BytesIO(res.content)) im.save("snapshot.jpg") 3.2 备注 鉴权使用的方法是通用 HTTP 认证中的 Digest 模式,详细信息可参考: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/WWW-Authenticate 针对我们的设备,要完成鉴权,需要从设备端返回的要求鉴权报文中提取 WWW-Authenticate 字段的 realm 和 nonce 的值(对下图而言,就是 TP-LINK IP-Camera 和 d48bbb82dfa9e1e543af852b466d5612): 然后根据上述两个值以及用户名(应为 admin)和密码来生成鉴权字段 Authorization,该字段由以下几个部分组成(其中绿色部分代表恒定不变的,蓝色部分代表从请求鉴权的报文中获取的,橙色部分代表需要实时计算的(下面将说明计算方法)。例如参考上图, 应为 TP-LINK IP-Camera,而 应为 d48bbb82dfa9e1e543af852b466d5612): Digest username=”admin”,”realm=””,nonce=””,uri=”/snapshot.jpg”,response=”” response 的计算方法(MD5 表示 MD5 加密): response = MD5(MD5(::)::MD5(:)) 例如,对于上述请求鉴权报文而言,有 变量 值 username admin realm TP-LINK IP-Camera password tp123456 nonce d48bbb82dfa9e1e543af852b466d5612 request-method GET url /snapshot.jpg 所以最终有 response = MD5(MD5(admin:TP-LINK IP-Camera:tp123456):d48bbb82dfa9e1e543af852b466d5612:MD5(GET:/snapshot.jpg)) = 8326b7d2f53557bdde755f9899673b6c