首页 新闻 会员 周边 捐助

用HttpWebRequest访问带摘要认证的web api,为什么每次访问都是先返回401?

0
悬赏园豆:100 [已解决问题] 解决于 2014-12-26 09:27

先看代码

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。

lenya的主页 lenya | 初学一级 | 园豆:131
提问于:2014-12-19 16:22
< >
分享
最佳答案
0

这个玩意 Credentials 叫做凭据,通过 Digest Authentication 后,服务器会给客户端颁发一个 Token (令牌,通常以 Cookie 的形式返回给客户端),客户端应该将此 Token 缓存起来,在以后的每次请求时都携带上。

收获园豆:100
Launcher | 高人七级 |园豆:45050 | 2014-12-19 17:59

是返回类似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 | 园豆:131 (初学一级) | 2014-12-19 21:18

@lenya: 服务器收到客户端的 Authorization 后,如果授权通过,就给客户端颁发 Token (放在Cookie 中),之后的每次请求对 Token 做验证。

服务器返回的 WWW-Authenticate 标头,不是 Token,而是挑战(Challenge),这是 Digest 认证的一部分。

Launcher | 园豆:45050 (高人七级) | 2014-12-22 09:22

@Launcher: 具体到我的问题,该怎么实现一次认证以后(或者说一段时间内)不需要再认证呢?

lenya | 园豆:131 (初学一级) | 2014-12-22 20:19

@lenya: 第二次不要创建新的HttpWebRequest

喵喵喵猫 | 园豆:1742 (小虾三级) | 2014-12-23 10:02

@lenya: 为你的 Login.aspx 启用 Digest 认证,其它页面启用 Forms Authentication,Digest 挑战成功后为响应设置 Cookie,客户端保存此 Cookie,在接下来的请求中都附带上此  Cookie。

Launcher | 园豆:45050 (高人七级) | 2014-12-23 10:12

@芬达: 这样第二次执行

var response = (HttpWebResponse)request.EndGetResponse(ar);

var stream = response.GetResponseStream();//这里会抛异常:无法访问已释放的对象。对象名:“System.Net.HttpWebResponse”。

lenya | 园豆:131 (初学一级) | 2014-12-24 09:07
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册