摘要访问认证是一种协议规定的Web服务器用来同网页浏览器进行认证信息协商的方法。它在密码发出前,先对其应用哈希函数,这相对于HTTP基本认证发送明文而言,更安全。从技术上讲,摘要认证是使用随机数来阻止进行密码分析的MD5加密哈希函数应用。它使用HTTP协议。

 

一、摘要认证基本流程:

 

1.客户端请求 (无认证)

Html代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

  1. GET /dir/index.html HTTP/1.0  
  2. Host: localhost  

 

2.服务器响应

服务端返回401未验证的状态,并且返回WWW-Authenticate信息,包含了验证方式Digest,realm,qop,nonce,opaque的值。其中:

Digest:认证方式;

realm:领域,领域参数是强制的,在所有的盘问中都必须有,它的目的是鉴别SIP消息中的机密,在SIP实际应用中,它通常设置为SIP代理服务器所负责的域名;

qop:保护的质量,这个参数规定服务器支持哪种保护方案,客户端可以从列表中选择一个。值 “auth”表示只进行身份查验, “auth-int”表示进行查验外,还有一些完整性保护。需要看更详细的描述,请参阅RFC2617;

nonce:为一串随机值,在下面的请求中会一直使用到,当过了存活期后服务端将刷新生成一个新的nonce值;

opaque:一个不透明的(不让外人知道其意义)数据字符串,在盘问中发送给用户。

 

Html代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

1. HTTP/1.0 401 Unauthorized  
2. Server: HTTPd/0.9  
3. Date: Sun, 10 Apr 2005 20:26:47 GMT  
4. WWW-Authenticate: Digest realm="testrealm@host.com",  
5. qop="auth,auth-int",  
6. nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",  
7. opaque="5ccc069c403ebaf9f0171e9517f40e41"

 

3.客户端请求  (用户名 "Mufasa", 密码 "Circle Of Life")

客户端接受到请求返回后,进行HASH运算,返回Authorization参数

其中:realm,nonce,qop由服务器产生;

uri:客户端想要访问的URI;

nc:“现时”计数器,这是一个16进制的数值,即客户端发送出请求的数量(包括当前这个请求),这些请求都使用了当前请求中这个“现时”值。例如,对一个给定的“现时”值,在响应的第一个请求中,客户端将发送“nc=00000001”。这个指示值的目的,是让服务器保持这个计数器的一个副本,以便检测重复的请求。如果这个相同的值看到了两次,则这个请求是重复的;

cnonce:这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护;

response:这是由用户代理软件计算出的一个字符串,以证明用户知道口令。

Html代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

1. <strong>response计算过程:</strong>  
2. HA1=MD5(A1)=MD5(username:realm:password)  
3. 如果 qop 值为“auth”或未指定,那么 HA2 为  
4. HA2=MD5(A2)=MD5(method:digestURI)  
5. 如果 qop 值为“auth-int”,那么 HA2 为  
6. HA2=MD5(A2)=MD5(method:digestURI:MD5(entityBody))  
7. 如果 qop 值为“auth”或“auth-int”,那么如下计算 response:  
8. response=MD5(HA1:nonce:nonceCount:clientNonce:qop:HA2)  
9. 如果 qop 未指定,那么如下计算 response:  
10. response=MD5(HA1:nonce:HA2)

 

 请求头:

Html代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

1. GET /dir/index.html HTTP/1.0  
2. Host: localhost  
3. Authorization: Digest username="Mufasa",  
4. realm="testrealm@host.com",  
5. nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",  
6. uri="/dir/index.html",  
7. qop=auth,  
8. nc=00000001,  
9. cnonce="0a4f113b",  
10. response="6629fae49393a05397450978507c4ef1",  
11. opaque="5ccc069c403ebaf9f0171e9517f40e41"

 

4.服务器响应

当服务器接收到摘要响应,也要重新计算响应中各参数的值,并利用客户端提供的参数值,和服务器上存储的口令,进行比对。如果计算结果与收到的客户响应值是相同的,则客户已证明它知道口令,因而客户的身份验证通过。

 

Html代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

  1. HTTP/1.0 200 OK  

 

二、服务端验证

编写一个自定义消息处理器,需要通过System.Net.Http.DelegatingHandler进行派生,并重写SendAsync方法。

 

C#代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

1. public class AuthenticationHandler : DelegatingHandler  
2. {  
3. protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)  
4.     {  
5. try  
6.         {  
7.             HttpRequestHeaders headers = request.Headers;  
8. if (headers.Authorization != null)  
9.             {  
10. new Header(request.Headers.Authorization.Parameter, request.Method.Method);  
11.   
12. if (Nonce.IsValid(header.Nonce, header.NounceCounter))  
13.                 {  
14. // Just assuming password is same as username for the purpose of illustration  
15. string password = header.UserName;  
16.   
17. string ha1 = String.Format("{0}:{1}:{2}", header.UserName, header.Realm, password).ToMD5Hash();  
18.   
19. string ha2 = String.Format("{0}:{1}", header.Method, header.Uri).ToMD5Hash();  
20.   
21. string computedResponse = String.Format("{0}:{1}:{2}:{3}:{4}:{5}",  
22. "auth", ha2).ToMD5Hash();  
23.   
24. if (String.CompareOrdinal(header.Response, computedResponse) == 0)  
25.                     {  
26. // digest computed matches the value sent by client in the response field.  
27. // Looks like an authentic client! Create a principal.  
28. new List<Claim>  
29.                         {  
30. new Claim(ClaimTypes.Name, header.UserName),  
31. new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.Password)  
32.                         };  
33.   
34. new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, "Digest") });  
35.   
36.                         Thread.CurrentPrincipal = principal;  
37.   
38. if (HttpContext.Current != null)  
39.                             HttpContext.Current.User = principal;  
40.                     }  
41.                 }  
42.             }  
43.   
44. base.SendAsync(request, cancellationToken);  
45.   
46. if (response.StatusCode == HttpStatusCode.Unauthorized)  
47.             {  
48. new AuthenticationHeaderValue("Digest",Header.UnauthorizedResponseHeader.ToString()));  
49.             }  
50.   
51. return response;  
52.         }  
53. catch (Exception)  
54.         {  
55.             var response = request.CreateResponse(HttpStatusCode.Unauthorized);  
56. new AuthenticationHeaderValue("Digest",Header.UnauthorizedResponseHeader.ToString()));  
57.   
58. return response;  
59.         }  
60.     }  
61. }

 

 Header类

 

C#代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

1. public class Header  
2. {  
3. public Header() { }  
4.   
5. public Header(string header, string method)  
6.     {  
7. string keyValuePairs = header.Replace("\"", String.Empty);  
8.   
9. foreach (string keyValuePair in keyValuePairs.Split(','))  
10.         {  
11. int index = keyValuePair.IndexOf("=", System.StringComparison.Ordinal);  
12. string key = keyValuePair.Substring(0, index);  
13. string value = keyValuePair.Substring(index + 1);  
14.   
15. switch (key)  
16.             {  
17. case "username": this.UserName = value; break;  
18. case "realm": this.Realm = value; break;  
19. case "nonce": this.Nonce = value; break;  
20. case "uri": this.Uri = value; break;  
21. case "nc": this.NounceCounter = value; break;  
22. case "cnonce": this.Cnonce = value; break;  
23. case "response": this.Response = value; break;  
24. case "method": this.Method = value; break;  
25.             }  
26.         }  
27.   
28. if (String.IsNullOrEmpty(this.Method))  
29. this.Method = method;  
30.     }  
31.   
32. public string Cnonce { get; private set; }  
33. public string Nonce { get; private set; }  
34. public string Realm { get; private set; }  
35. public string UserName { get; private set; }  
36. public string Uri { get; private set; }  
37. public string Response { get; private set; }  
38. public string Method { get; private set; }  
39. public string NounceCounter { get; private set; }  
40.   
41. // This property is used by the handler to generate a  
42. // nonce and get it ready to be packaged in the  
43. // WWW-Authenticate header, as part of 401 response  
44. public static Header UnauthorizedResponseHeader  
45.     {  
46. get  
47.         {  
48. return new Header()  
49.             {  
50. "MyRealm",  
51.                 Nonce = WebApiDemo.Nonce.Generate()  
52.             };  
53.         }  
54.     }  
55.   
56. public override string ToString()  
57.     {  
58. new StringBuilder();  
59. "realm=\"{0}\"", Realm);  
60. ",nonce=\"{0}\"", Nonce);  
61. ",qop=\"{0}\"", "auth");  
62. return header.ToString();  
63.     }
  1. }  

 nonce类

 

C#代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

1. public class Nonce  
2. {  
3. private static ConcurrentDictionary<string, Tuple<int, DateTime>>  
4. new ConcurrentDictionary<string, Tuple<int, DateTime>>();  
5.   
6. public static string Generate()  
7.     {  
8. byte[] bytes = new byte[16];  
9.   
10. using (var rngProvider = new RNGCryptoServiceProvider())  
11.         {  
12.             rngProvider.GetBytes(bytes);  
13.         }  
14.   
15. string nonce = bytes.ToMD5Hash();  
16.   
17. new Tuple<int, DateTime>(0, DateTime.Now.AddMinutes(10)));  
18.   
19. return nonce;  
20.     }  
21.   
22. public static bool IsValid(string nonce, string nonceCount)  
23.     {  
24. int, DateTime> cachedNonce = null;  
25. //nonces.TryGetValue(nonce, out cachedNonce);  
26. out cachedNonce);//每个nonce只允许使用一次  
27.   
28. if (cachedNonce != null) // nonce is found  
29.         {  
30. // nonce count is greater than the one in record  
31. if (Int32.Parse(nonceCount) > cachedNonce.Item1)  
32.             {  
33. // nonce has not expired yet  
34. if (cachedNonce.Item2 > DateTime.Now)  
35.                 {  
36. // update the dictionary to reflect the nonce count just received in this request  
37. //nonces[nonce] = new Tuple<int, DateTime>(Int32.Parse(nonceCount), cachedNonce.Item2);  
38.   
39. // Every thing looks ok - server nonce is fresh and nonce count seems to be   
40. // incremented. Does not look like replay.  
41. return true;  
42.                 }  
43.                      
44.             }  
45.         }  
46.   
47. return false;  
48.     }  
49. }

C#代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

1. [Authorize]  
2. public class ProductsController : ApiController

C#代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

1. GlobalConfiguration.Configuration.MessageHandlers.Add(  
2. new AuthenticationHandler());

三、客户端的调用

这里主要说明使用WebClient调用

C#代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

1. public static string Request(string sUrl, string sMethod, string sEntity, string sContentType,  
2. out string sMessage)  
3. {  
4. try  
5.     {  
6. "";  
7. using (System.Net.WebClient client = new System.Net.WebClient())  
8.         {  
9.             client.Credentials = CreateAuthenticateValue(sUrl);  
10.             client.Headers = CreateHeader(sContentType);  
11.   
12. new Uri(sUrl);  
13. byte[] bytes = Encoding.UTF8.GetBytes(sEntity);  
14. byte[] buffer;  
15. switch (sMethod.ToUpper())  
16.             {  
17. case "GET":  
18.                     buffer = client.DownloadData(url);  
19. break;  
20. case "POST":  
21. "POST", bytes);  
22. break;  
23. default:  
24. "POST", bytes);  
25. break;  
26.             }  
27.   
28. return Encoding.UTF8.GetString(buffer);  
29.         }  
30.     }  
31. catch (WebException ex)  
32.     {  
33.         sMessage = ex.Message;  
34. as HttpWebResponse;  
35.         var httpStatusCode = rsp.StatusCode;  
36. "WWW-Authenticate");  
37.   
38. return "";  
39.     }  
40. catch (Exception ex)  
41.     {  
42.         sMessage = ex.Message;  
43. return "";  
44.     }  
45. }

C#代码  

java app接口安全验证 webapi接口安全性验证_java app接口安全验证

1. private static CredentialCache CreateAuthenticateValue(string sUrl)  
2. {  
3. new CredentialCache();  
4. new Uri(sUrl), "Digest", new NetworkCredential("Lime", "Lime"));  
5.   
6. return credentialCache;  
7. }

网站网址:lscfan.com