Http认证分成按顺序两部分,一是服务器认证要求,二是客户端授权信息。
服务器通过对客户端的请求返回状态码401,并在WWW-Authenticate头字段说明认证要求,客户端应重复上一次的请求并增加Authorization头字段提供授权信息。
代理服务器也可以通过返回状态码407,并在Proxy-Authenticate头字段说明认证要求,客户端应重复上一次的请求并提供Proxy-Authorization头字段提供授权信息。
以上两个场景可能同时存在,即先收到407,又收到401。
认证的类型在认证要求中说明,如Basic,Digest
'''
WWW-Authenticate: Basic realm="noname@noname.com"
'''
相应的授权信息中应提供相同的说明
'''
Authorization: Basic QsdfgWGHffuIcaNlc2FtZQ==
'''
Basic类型的认证非常简单,只需要提供Base64编码的用户名和密码。 生成方式如下:
'''
Base64("username:passworld")
'''
Digest类型需要用到服务器提供的几个信息, 一个认证要求如下:
'''
WWW-Authenticate: Digest
realm="testrealm@host.com",
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
'''
客户端要复制以上信息,同时提供username, response,uri, cnonce, nc这几个参数。例如
'''
Authorization: Digest
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
qop=auth,
opaque="5ccc069c403ebaf9f0171e9517f40e41",
username="someone",
uri="/dir/index.html",
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1"
'''
其中username, uri是静态信息,客户端可以直接缓存。cnonce是一个随机数,nc和response需要计算得到。
其中认证请求中qop这个项决定了认证的复杂度,如果没有qop则不需要cnonce,cnonce也不参与计算。
当有qop=auth时,客户端也认证服务器,方法是提供cnonce/nc参与计算;当qop=authint时,客户端不光要把uri
参与计算,消息主体也要参与计算,因此下文中消息主体表示为qop。
为简单起见,这里讨论最复杂的情况,其它情况下,对应的计算结果为空值。
nonce和cnonce可能由以下算法根据时间(time-stamp)得到,当然,也可以用真正的随机发生器。
'''
Base64(time-stamp H(time-stamp:Etag:private-key))
'''
time-stamp为时钟,
ETag为请求的Etag头,
private-key为服务器知道的一个值,
H为一种摘要算法,可能是MD5。
nc是nonce-count,即对服务器侧nonce的计数。如果重复同一个请求,计数加1,如果是新的请求,计算
从1开始,它要求有32bit长,编码为HEX。
真正的授权信息是response,它的计算方式如下:
'''
H(H(A1):nonce:nc:cnonce:qop:H(A2))
'''
当没有qop时
'''
H(H(username:realm:passwd):nonce:H(A2))
'''
A1有两种情况,一是有session,一是没有session。没有session时,直接使用
'''
username:realm:passwd
'''
有session时,增加nonce和cnonce,使在服务器和客户端的一次连接中唯一。
'''
H(username:realm:passwd):nonce:cnonce
'''
A2部分根据qop是否等于authint,决定是否需要计算entity-body
'''
Method:digest-uri:H(entity-body)
'''