HTTP 提供了一套访问控制和认证的通用框架。最常用的 HTTP 认证方案是 HTTP Basic Authentication。

HTTP 常规的认证流程

HTTP 通用的认证模型非常简单,就是质询/响应 (challenge/response) 框架。以 HTTP Basic Authentication 为例:

  • 当客户端请求一个需要认证的资源时,服务器返回一条 401 (Unauthorized) 响应。并在 WWW-Authenticate 响应首部携带如何进行验证的相关信息,其中至少包含一种质询方式。
  • 然后,客户端根据WWW-Authentication提供的认证要求,在请求中添加Authorization首部字段进行认证,值为相应认证方案的认证凭证 (如 Basic 认证方案:Basic + “用户名:密码”的 base64 编码)。
  • 服务器验证通过,则返回正常的请求。如果认证错误,则返回 403 (Forbidden)。

代理认证 (Proxy Authentication)

服务器可以委托代理服务器提供内部资源的统一访问控制,即代理认证。
如果使用了 HTTP 代理服务器,客户端的访问的其实是代理服务器,请求先经过代理服务器,然后代理服务器根据请求的内容处理请求 (如,过滤掉请求或将请求转发到不同的服务器等等)。如果你的网站是发布到公网上的话,你的代理服务器就暴露在公网上了。如果你的代理服务器没有设置任何访问权限,就可能会被网上的大量代理扫描程序扫描出来,公布到各大免费代理站点。所以,代理服务器最好设置账号密码,而且不要是弱密码。

代理认证与 Basic 认证的过程类似,同样的基于 HTTP 质询/响应框架。

Basic Authentication Proxy Authentication
401 407
WWW-Authenticate Proxy-Authenticate
Authorization Proxy-Authorization

WWW-Authenticate 与 Proxy-Authenticate

WWW-AuthenticateProxy-Authenticate响应头部指定了获取资源访问权限的身份验证方法。

1
2
WWW-Authenticate: <type> realm=<realm>
Proxy-Authenticate: <type> realm=<realm>

  • <type>:指验证的类型,如 Basic, Bearer, Digest, HOBA, Mutual等。
  • <realm>:保护区域的描述,或指定保护的范围。如果未指定 realm,通常客户端显示一个格式化的主机名来代替。

Authorization 与 Proxy-Authorization

AuthorizationProxy-Authorization请求消息首部,包含用来向 (代理) 服务器验证身份的凭证。同样需要指明验证的类型,其后跟着验证信息。验证信息可以被编码或者加密,取决于采用的是哪种验证类型。

1
2
Authorization: <type> <credentials>
Proxy-Authorization: <type> <credentials>

Nginx 搭建 HTTP Basic Authentication

Nginx ngx_http_auth_basic_module 可以实现 HTTP Basic Authentication 协议。

1
2
3
4
location / {
auth_basic 'authentication';
auth_basic_user_file conf.d/htpasswd;
}

  • auth_basic: string | off,默认 off,不开启。开启 HTTP Basic Authentication 只需要填写一个字符串,该字符串会作为 realm 的值,在服务器质询客户端的返回头部信息 WWW-Authenticate 中显示,如:WWW-Authenticate: Basic realm="Authorication"

  • auth_basic_user_file: 指定访问的用户名和密码的配置文件,可以是相对路径或绝对路径的文件。当使用相对路径时,则相对 nginx 安装配置目录,如/etc/nginx//etc/nginx/conf.d/试了下都可以。文件配置内容格式,如htpasswd

    1
    2
    3
    # comment
    name1:password1
    name2:password2:comment
    • 可以用 # 注释
    • 可以设置多个用户名和密码,每行一个,用户名和密码之间用 : 隔开,注释也可以写在密码后面,用 : 隔开。
    • 用户名直接填写,但是密码需要经过加密。如,Linux 系统,密码 123456 可以用 openssl passwd 123456 生成。

假设,刚刚搭建的服务器地址为:127.0.0.1。从浏览器访问 http://127.0.0.1 就会收到一个 401 质询,弹出一个用户名和密码的输入框。用户输入账号和密码之后,浏览器会用 : 将其连接起来,编成 base64 编码,然后将其放在请求头部的 Authorization 中发送给服务端。服务端验证通过后就可以正常浏览页面的内容了。

HTTP 基础认证过程

假如,nginx 配置好的认证页面为 http://127.0.0.1

  • telnet 建立一条 http 连接

    1
    telnet 127.0.0.1 80
  • 发送正常的请求,会返回 401

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    HEAD / HTTP/1.1
    Host: 172.16.105.114

    HTTP/1.1 401 Unauthorized
    Server: BDSCAN/1.5.0
    Date: Wed, 17 Jan 2018 08:26:04 GMT
    Content-Type: text/html
    Content-Length: 195
    Connection: keep-alive
    WWW-Authenticate: Basic realm="authentication"
  • 发送带验证信息的请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    HEAD / HTTP/1.1
    Host: 172.16.105.114
    Authorization: Basic bGl6czoxMjM0NTY=

    HTTP/1.1 200 OK
    Server: BDSCAN/1.5.0
    Date: Wed, 17 Jan 2018 08:26:59 GMT
    Content-Type: text/html
    Content-Length: 334
    Last-Modified: Tue, 16 Jan 2018 08:55:10 GMT
    Connection: keep-alive
    ETag: "5a5dbdee-14e"
    Accept-Ranges: bytes

验证信息放在请求头的 Authorization,即 Authorization: Basic base64-username-and-passwordbase64-username-and-password 为用户名和密码用 : 连接起来,并经过 base64 编码等到的字符串 (即上面的 bGl6czoxMjM0NTY=) 。Python(python2) 生成:

1
2
3
4
5
import base64

username = 'lizs'
password = '123456'
msg = base64.b64encode('%s:%s' % (username, password))