📘 第四章 返回结果的HTTP状态码
📄 一句话概括

401 是「你还没证明你是谁」。响应里必须带 WWW-Authenticate,告诉客户端该用哪种方式证明。

📝 本节和前面几节的关系

上一节的 400 是请求写坏了,这一节是请求没写坏但没带身份。 第八章会展开讲各种认证方案,这里先把 401 这个码本身说清。

一、名字其实取错了

401 叫 Unauthorized(未授权),但它真正的意思是未认证(unauthenticated)——你还没说你是谁。 真正的「未授权」是 403。 这个命名失误在 RFC 里被承认过,但名字改不了了。所以看到 401,永远理解成「去带上凭据再来」。 它必须带 WWW-Authenticate 首部,否则客户端不知道该用 Basic、Bearer 还是别的。 缺了这个首部的 401 是不合规的——客户端只能猜。

二、Basic 认证:两句话说完

客户端把 用户名:密码 用 base64 编一下塞进 Authorization: Basic xxx。服务端解出来比对。 就这两步。 Demo 里 bozai:secret 是 12 字节,base64 之后 16 字节,涨到 1.33 倍。 这个系数是固定的(3 字节变 4 字符)。 关键在于:base64 是编码不是加密。任何人都能一键解回来。 所以 Basic 认证必须跑在 HTTPS 上,否则等于把密码明文广播。这句话每年都还有人踩。

三、今天更常用的是 Bearer

Authorization: Bearer <token>,token 通常是 OAuth 2.0 或 JWT 签发的。 比起 Basic,它有几个好处:不暴露密码、可以带过期时间、可以按权限范围授权。 对应的 WWW-Authenticate 会写成 Bearer realm="...", error="invalid_token", 比 Basic 那一种能表达更多信息。 书里只讲了 Basic 和 Digest(8.2、8.3 节),Bearer 是这本书之后才成为主流的。 2026 年写新系统,默认应该用 Bearer,Basic 只在内部工具和临时调试里出现。

四、动手 Demo:两步走:先被拒,再带凭据

服务端第一次回 401 加 WWW-Authenticate: Basic realm="MySite",客户端构造 base64 凭据再发一次。

if not auth:
    return (u'HTTP/1.1 401 Unauthorized',
            [(u'WWW-Authenticate', u'Basic realm="MySite"')], b'')
tok = base64.b64encode(u'bozai:secret'.encode()).decode()
s.sendall(_req(extra=[(u'Authorization', u'Basic ' + tok)]))

实跑输出:

--------------------------------------------------------------------
401 Unauthorized:先问你是谁,再给你东西
--------------------------------------------------------------------
  第一次(不带凭据)         HTTP/1.1 401 Unauthorized
    WWW-Authenticate         Basic realm="MySite", charset="UTF-8"
  第二次(带凭据)           HTTP/1.1 200 OK
    返回                     欢迎回来

  原文                       bozai:secret(12 字节)
  base64 之后                Ym96YWk6c2VjcmV0(16 字节)
  膨胀                       1.33 倍
  关键事实                   base64 是**编码不是加密**,任何人都能解回来
  Basic 认证只做两件事:把用户名密码拼起来、base64 一下。
  所以它**必须跑在 HTTPS 上**,否则等于明文广播密码。
  401 与 403 的区别:401 是「你没证明你是谁」,403 是「知道你是谁,但不许看」。

第一次(不带凭据)401、 WWW-Authenticate Basic realm="MySite", charset="UTF-8"——这一行就是「该用哪种方式证明」的说明书。 第二次(带凭据)200 + 返回 欢迎回来。凭据正确,放行。 原文 bozai:secret(12 字节) → base64 之后 Ym96YWk6c2VjcmV0(16 字节),膨胀 1.33 倍。 这一行是本节的硬结论:base64 不提供任何保密性。 顺带一个真实细节:realm 只能是 ASCII。 我第一版写成中文,服务端编 utf-8、客户端按 latin-1 解, 直接乱码——协议里的字符串多半限定 ASCII。

30 秒实验

  1. 把凭据故意写错,看服务端会不会回 401(会),浏览器会不会重新弹登录框(会, 因为带 WWW-Authenticate)。
  2. 把 base64 那串直接贴到纸上解码,确认密码明文可见。
  3. 想想:为什么 Basic 认证不能不带 HTTPS?(因为密码就是明文)

常见误区

  1. 「base64 是加密」——是编码,一秒就能解。
  2. 「401 表示没权限」——401 表示没认证,没权限是 403。
  3. 「401 可以不带 WWW-Authenticate」——不合规,客户端无从下手。
自测三问(点开对答案) 1. 401 与 403 的核心区别? —— 401 是未认证(你是谁还不知道),403 是未授权(知道你是谁但不许)。 2. 401 响应必须带哪个首部? —— `WWW-Authenticate`,说明该用哪种认证方式。 3. 为什么 Basic 认证必须配 HTTPS? —— 因为 base64 可逆,凭据等同于明文传输。

401 是「不知道你是谁」。下一节看「知道你是谁,但不给你」。

相关笔记