先看代码
static void Main(string[] args) { //string url = Path.Combine(GetString(Resource.String.ApiRootUrl), "MenuItem"); string url = "http://172.17.10.23:7892/api/customer"; try { for (int i = 0; i < 5; i++) { var httpRequest = (HttpWebRequest)HttpWebRequest.Create(url); httpRequest.Credentials = GetCredentialCache(url); var a = httpRequest.BeginGetResponse(ProcessWebResponse, httpRequest); a.AsyncWaitHandle.WaitOne(); } } catch (WebException webEx) { ..... } catch (Exception ex) { ...... } Console.ReadKey(); } static CredentialCache _credentialCache = null; static CredentialCache GetCredentialCache(string sUrl) { if (_credentialCache == null) { _credentialCache = new CredentialCache(); _credentialCache.Add(new Uri(sUrl), "Digest", new NetworkCredential("Lime", "Lime", "RealmOfBadri")); } return _credentialCache; }
Fiddler捕获到的请求
为什么每一次正常请求都会先返回401呢?有什么办法只在首次访问时要求授权验证,后续就延用该认证,像浏览器访问一样。或是有其他的方法实现?服务端的认证方式为Digest Authentication。
这个玩意 Credentials 叫做凭据,通过 Digest Authentication 后,服务器会给客户端颁发一个 Token (令牌,通常以 Cookie 的形式返回给客户端),客户端应该将此 Token 缓存起来,在以后的每次请求时都携带上。
是返回类似Token的东西,不过不是通过Cookie的形式返回的。第一次(未授权)请求,服务器返回类似
WWW-Authenticate: Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"
然后客户端根据real,qop,nonce等计算经过一个稍微复杂的hash运算,再把值通过RequestHead的方式发回给服务器,如
Authorization: Digest username="Mufasa", realm="testrealm@host.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/dir/index.html", qop=auth, nc=00000001, cnonce="0a4f113b", response="6629fae49393a05397450978507c4ef1", opaque="5ccc069c403ebaf9f0171e9517f40e41"
客户端每次发送的值都是经过计算的,每次都不一样,所以不能直接保存下来,下次重复发送该“Token”,现在是想通过Credentials 自动计算,并附加到请求头中。现在的问题是非首次访问不会自动带上该请求头,每次都会被服务器拒绝一下(401),再把信息带上。
@lenya: 服务器收到客户端的 Authorization 后,如果授权通过,就给客户端颁发 Token (放在Cookie 中),之后的每次请求对 Token 做验证。
服务器返回的 WWW-Authenticate 标头,不是 Token,而是挑战(Challenge),这是 Digest 认证的一部分。
@Launcher: 具体到我的问题,该怎么实现一次认证以后(或者说一段时间内)不需要再认证呢?
@lenya: 第二次不要创建新的HttpWebRequest
@lenya: 为你的 Login.aspx 启用 Digest 认证,其它页面启用 Forms Authentication,Digest 挑战成功后为响应设置 Cookie,客户端保存此 Cookie,在接下来的请求中都附带上此 Cookie。
@芬达: 这样第二次执行
var response = (HttpWebResponse)request.EndGetResponse(ar);
var stream = response.GetResponseStream();//这里会抛异常:无法访问已释放的对象。对象名:“System.Net.HttpWebResponse”。