一 无鉴权方式
就是正确的客户端请求发到服务器后,HTTP服务器返回200状态码并且把内容直接返回。
报文示例:
请求:
GET.http://10.127.194.3:8061/VoiceObjects.....
返回:
HTTP/1.1.200.OK..Date:.Fri,.28.Oct.20.....
二 基本鉴权方式
基本鉴权和摘要鉴权方式都是基于一种叫challenge-response鉴权模式(challenge-response authentication mechanism)来运作的。这里先解释一下这种鉴权模式,它是这么一种请求-响应顺序:
A Client发送“无鉴权”请求给Server
B Server给Client返回401(Unauthorized),并附上Server需要的鉴权方式(基本鉴权或摘要鉴权),和realm(下面会解释),和其他信息给Client。
C Client基于Server给回的信息重新组织请求消息,带上鉴权信息再次发送请求。
基本鉴权和摘要鉴权都是基于上面的模式运作的,但到实际应用中,也不一定全按照上面的顺序操作,比如如果双方早就约定好要用某种鉴权方式,就可以省略去A和B的步骤一步到位。
Realm:这个在基本鉴权和摘要鉴权里面都会附上,这是服务器指定的鉴权分类标志(不知道如何用中文更好地表达了,原文:These realms allow the protected resources on a server to bepartitioned into a set of protection spaces, each with its own authenticationscheme and/or authorization database.),就是说Client访问不同的链接,有可能会接收到不同的Realm标志,从而根据接受到的不同Realm值,用不同的用户名和密码或其他鉴权信息来组织下一个请求。
基本鉴权要求:
当Server要求基本鉴权的时候,它必须在返回的401消息中带上“WWW-Authenticate”头域,后接一个“Basic”关键字,再后面接一个realm和realm值,中间用空格分开:
challenge = "Basic" realm
realm = "realm" "="realm-value
realm-value = quoted-string
Client收到消息的时候,要带上“Authorization”头域,后接一个“Basic”关键字,再后面接用“:”号相连的用户名和密码的base64加密字符串:
credentials = "Basic"basic-credentials
basic-credentials = base64-user-pass
base64-user-pass =
user-pass = userid ":" password
userid = *
password = *TEXT
报文示例:
请求尝试:
GET.http://10.127.194.3:8061/VoiceObjects/...
请求尝试回复:
HTTP/1.1.401.Unauthorized..Date:.Thu,.20.Oct.2011.15:16:22.GMT..Server:.Jetty/5.1.4.(SunOS/5.10.sparc.java/1.5.0_15..WWW-Authenticate:.basic.realm="VORealm"..Content-Type:.text/html..Content-Length:.1249.......
再次请求:
GET.http://10.127.194.3:8061/VoiceObjects/sfdsdf.HTTP/1.1..Authorization:.Basic.c2ljYXA6c2ljYXAxMjM=..Connection:.Keep-Alive..Host:.10.127.194.3:8061..Content-Length:.0....
再次请求回复:
HTTP/1.1.200.OK..Date:.Thu,.20.Oct.2011.15:12:36.GMT..Server:.
三 摘要鉴权方式:
上面已经说了摘要鉴权方式类似于请求鉴权方式,只不过摘要鉴权方式有更复杂的计算方式。
开始之前,解释一下下面要用到的表达方式:
H(data) =MD5(data)
----这个表示把data用MD5算法加密。
KD(secret, data)= H(concat(secret, ":", data))
----这个表示把secret和data用”:”号相连接后,再用MD5算法加密。
----secret和data都是某字符串。
如果Server回复401,则在摘要鉴权下,应该带下面这些额外信息:
challenge = "Digest" digest-challenge
digest-challenge = 1#( realm | [ domain ] | nonce |
[ opaque ] |[ stale ]| [ algorithm ] |
[ qop-options ] |[auth-param] )
domain = "domain" "="<"> URI ( 1*SP URI ) <">
URI = absoluteURI | abs_path
nonce = "nonce" "="nonce-value
nonce-value = quoted-string
opaque = "opaque" "="quoted-string
stale = "stale" "="( "true" | "false" )
algorithm = "algorithm" "="( "MD5" | "MD5-sess" |
token )
qop-options = "qop" "="<"> 1#qop-value <">
qop-value = "auth" |"auth-int" | token
下面一一解释一下每个值的含义:
A realm: 跟摘要鉴权意义差不多,不再赘述。
B domain: 是双引号引住的一个URI地址列表,每个URI之间用空格分开,比如”http://www.baidu.com /value/index.html”,就表示了两个地址,一个是http://www.baidu.com,一个是/value/index.html。domain的意义是标志此次返回的401里带上的信息可以用在哪些URI里面去?
C nonce: 这个是摘要鉴权中非常重要的值,它是一串由Server生成的base64加密或16进制的字符串(this string be base64 or hexadecimal data),在摘要鉴权流程中,每次Server产生401,就要返回一个不同的nonce值。标准推荐了一个算法:time-stampH(time-stamp ":" ETag ":" private-key),time-stamp是Server产生nonce时的时间挫;ETag是HTTP解决缓存机制的方式,通常也是一个奇怪的串,但可以用来标示被请求的文件的版本;private-key是只有Server才知道的私有Key。一般情况下,每个HTTP服务器都是有配置能自动生成nonce值的而不用我们写代码。Client一定要使用这个值进行请求内容的计算,下面会介绍。
额外说一下,我们看nonce值里面会有时间挫,这个时间也会被Server用来判断这个nonce是什么时候生成的,到现在还有没有过期?Client会在请求信息中带上这个值,发到Server端,Server端拿到这个nonce值,解码或者不解码,反正Server产生的,Server就知道如何处理这个nonce,并用它带的信息来判断这个请求是否有效。
D opaque: 一串由Server指定的字符串,Client在相同的保护空间(protection space,就是一堆使用相同用户名和密码的链接)内发送的请求,就把opaque原封不动地返回给Server就可以了。
E stale: 用来指示Client的前一个请求里的nonce是否过期了。
TRUE:
表示用户名和密码是正确的,但nonce过期了。
FALSE:
表示nonce是正确的,但用户名和密码不正确,这时候,Client要换一个用户名和密码重新发送请求(对IE来说,就是弹出输入用户名和密码的对话框)。
F algorithm: Server和Client算法约定,一般是MD5或"MD5-sess"。
G qop-options: 可选的,一般是"auth"或"auth-int"。祥见Client的算法部分。
H auth-param: 未来扩展使用。
当Client接受到上面的信息后,就要再次准备请求消息,摘要鉴权的请求消息如下:
credentials = "Digest" digest-response
digest-response = 1#( username | realm | nonce | digest-uri
| response | [ algorithm] | [cnonce] |
[opaque] | [message-qop]|
[nonce-count] | [auth-param] )
username = "username" "="username-value
username-value = quoted-string
digest-uri = "uri" "="digest-uri-value
digest-uri-value = request-uri ; As specified by HTTP/1.1
message-qop = "qop" "=" qop-value
cnonce = "cnonce" "="cnonce-value
cnonce-value = nonce-value
nonce-count = "nc" "=" nc-value
nc-value = 8LHEX
response = "response" "="request-digest
request-digest = <"> 32LHEX<">
LHEX = "0" | "1" | "2" | "3" |
"4" |"5" | "6" | "7" |
"8" |"9" | "a" | "b" |
"c" |"d" | "e" | "f"
A username: 跟某个realm相关联的用户名;
B digest-uri: 就是HTTP头里的请求URI,拷贝一份到这里就可以了。拷贝的目的是可能会经过Proxy,把原始URI修改了。
C qop: Server给过来的值,如果Server指定了几个,那客户端就一定要选一个。这个字段的作用是指定了算response字符的算法。
D cnonce:如果Server指定了qop,那请求信息里一定要有这个值。如果Server没有指定qop,那一定不能带上这个值。这个值是由Client用自己的方式指定的一串字符串。
E nonce-count: 如果Server指定了qop,那请求信息里一定要有这个值。如果Server没有指定qop,那一定不能带上这个值。使用nonce的次数,包括此次。是二进制表示的次数。
F response: 这个是最重要的请求信息,它的计算方法如下:
如果"qop"值是"auth"或"auth-int":
request-digest = <"> < KD ( H(A1), unq(nonce-value)
":" nc-value
":" unq(cnonce-value)
":" unq(qop-value)
":" H(A2)
)<">
如果未指定qop值:
request-digest =
<"> < KD ( H(A1),unq(nonce-value) ":" H(A2) ) ><">
关于A1和A2,计算方法如下:
A1:
如果"algorithm"是MD5,那么
A1 = unq(username-value) ":"unq(realm-value) ":" passwd
如果"algorithm"是MD5-sess,那么A1只会在受到401后的所有请求的第一次请求中计算一次,计算方法如下:
A1 = H( unq(username-value) ":"unq(realm-value)
":" passwd )
":"unq(nonce-value) ":" unq(cnonce-value)
A2:
如果qop的值是"auth"或者未指定,则:
A2 = Method ":" digest-uri-value
如果qop的值是"auth-int",则:
A2 = Method ":" digest-uri-value":" H(entity-body)
Method是HTTP的请求方法,比如“Get”“POST”等,可以看HTTP协议文档的5.1.1章节;digest-uri-value =request-uri ; As specified by HTTP/1.1;entity-body在HTTP协议文档的7.2章节。
报文示例:
第一次:
GET.http://10.119.244.39:8099/VoiceObjects...
第二次:
HTTP/1.1.401.Unauthorized..Date:.Wed,.08.Dec.2010.17:16:17.GMT..Server:.Jetty/5.1.4.(SunOS/5.10.sparc.java/1.5.0_15..WWW-Authenticate:.Digest.realm="VORealm",.domain="/VoiceObjects",.nonce="csP7xiwBAAB4QgAH08FxchbbLhA1zeCy",.algorithm=MD5,.qop="auth"..Content-Type:.text/html..Content-Length:.1249...........
第三次:
GET.http://10.119.244.39:8099/VoiceObjects/sdfsdfg.HTTP/1.1..Authorization:.Digest.username="ggggg",.realm="VORealm",.nonce="csP7xiwBAAB4QgAH08FxchbbLhA1zeCy",.uri="/VoiceObjects/DialogMappingAUTH",.response="2d2caa1d1043150281b61e92322a26d8",.algorithm=MD5,.qop=auth,.nc=00000001,.cnonce="01343c114a9c5e842351ce3eaedc1ed1"..Connection:.Keep-Alive..Host:.10.119.244.39:8099..Content-Length:.0....
第四次:
HTTP/1.1.200.OK..Date:.Thu,.20.Oct.2011.15:12:36.GMT..Server:...