WebApi接口安全认证——HTTP之摘要认证
2016-06-04 18:14
387 查看
WebApi接口安全认证——HTTP之摘要认证
博客分类:
WebC#
webapiapiwebservice
摘要访问认证是一种协议规定的Web服务器用来同网页浏览器进行认证信息协商的方法。它在密码发出前,先对其应用哈希函数,这相对于HTTP基本认证发送明文而言,更安全。从技术上讲,摘要认证是使用随机数来阻止进行密码分析的MD5加密哈希函数应用。它使用HTTP协议。
一、摘要认证基本流程:
1.客户端请求 (无认证)
Html代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
GET /dir/index.html HTTP/1.0
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代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
HTTP/1.0 401 Unauthorized
Server: HTTPd/0.9
Date: Sun, 10 Apr 2005 20:26:47 GMT
WWW-Authenticate: Digest realm="testrealm@host.com",
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
3.客户端请求 (用户名 "Mufasa", 密码 "Circle Of Life")
客户端接受到请求返回后,进行HASH运算,返回Authorization参数
其中:realm,nonce,qop由服务器产生;
uri:客户端想要访问的URI;
nc:“现时”计数器,这是一个16进制的数值,即客户端发送出请求的数量(包括当前这个请求),这些请求都使用了当前请求中这个“现时”值。例如,对一个给定的“现时”值,在响应的第一个请求中,客户端将发送“nc=00000001”。这个指示值的目的,是让服务器保持这个计数器的一个副本,以便检测重复的请求。如果这个相同的值看到了两次,则这个请求是重复的;
cnonce:这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护;
response:这是由用户代理软件计算出的一个字符串,以证明用户知道口令。
Html代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
<strong>response计算过程:</strong>
HA1=MD5(A1)=MD5(username:realm:password)
如果 qop 值为“auth”或未指定,那么 HA2 为
HA2=MD5(A2)=MD5(method:digestURI)
如果 qop 值为“auth-int”,那么 HA2 为
HA2=MD5(A2)=MD5(method:digestURI:MD5(entityBody))
如果 qop 值为“auth”或“auth-int”,那么如下计算 response:
response=MD5(HA1:nonce:nonceCount:clientNonce:qop:HA2)
如果 qop 未指定,那么如下计算 response:
response=MD5(HA1:nonce:HA2)
请求头:
Html代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
4.服务器响应
当服务器接收到摘要响应,也要重新计算响应中各参数的值,并利用客户端提供的参数值,和服务器上存储的口令,进行比对。如果计算结果与收到的客户响应值是相同的,则客户已证明它知道口令,因而客户的身份验证通过。
Html代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
HTTP/1.0 200 OK
二、服务端验证
编写一个自定义消息处理器,需要通过System.Net.Http.DelegatingHandler进行派生,并重写SendAsync方法。
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
public class AuthenticationHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
HttpRequestHeaders headers = request.Headers;
if (headers.Authorization != null)
{
Header header = new Header(request.Headers.Authorization.Parameter, request.Method.Method);
if (Nonce.IsValid(header.Nonce, header.NounceCounter))
{
// Just assuming password is same as username for the purpose of illustration
string password = header.UserName;
string ha1 = String.Format("{0}:{1}:{2}", header.UserName, header.Realm, password).ToMD5Hash();
string ha2 = String.Format("{0}:{1}", header.Method, header.Uri).ToMD5Hash();
string computedResponse = String.Format("{0}:{1}:{2}:{3}:{4}:{5}",
ha1, header.Nonce, header.NounceCounter,header.Cnonce, "auth", ha2).ToMD5Hash();
if (String.CompareOrdinal(header.Response, computedResponse) == 0)
{
// digest computed matches the value sent by client in the response field.
// Looks like an authentic client! Create a principal.
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, header.UserName),
new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.Password)
};
ClaimsPrincipal principal = new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, "Digest") });
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
}
}
}
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Digest",Header.UnauthorizedResponseHeader.ToString()));
}
return response;
}
catch (Exception)
{
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Digest",Header.UnauthorizedResponseHeader.ToString()));
return response;
}
}
}
Header类
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
public class Header
{
public Header() { }
public Header(string header, string method)
{
string keyValuePairs = header.Replace("\"", String.Empty);
foreach (string keyValuePair in keyValuePairs.Split(','))
{
int index = keyValuePair.IndexOf("=", System.StringComparison.Ordinal);
string key = keyValuePair.Substring(0, index);
string value = keyValuePair.Substring(index + 1);
switch (key)
{
case "username": this.UserName = value; break;
case "realm": this.Realm = value; break;
case "nonce": this.Nonce = value; break;
case "uri": this.Uri = value; break;
case "nc": this.NounceCounter = value; break;
case "cnonce": this.Cnonce = value; break;
case "response": this.Response = value; break;
case "method": this.Method = value; break;
}
}
if (String.IsNullOrEmpty(this.Method))
this.Method = method;
}
public string Cnonce { get; private set; }
public string Nonce { get; private set; }
public string Realm { get; private set; }
public string UserName { get; private set; }
public string Uri { get; private set; }
public string Response { get; private set; }
public string Method { get; private set; }
public string NounceCounter { get; private set; }
// This property is used by the handler to generate a
// nonce and get it ready to be packaged in the
// WWW-Authenticate header, as part of 401 response
public static Header UnauthorizedResponseHeader
{
get
{
return new Header()
{
Realm = "MyRealm",
Nonce = WebApiDemo.Nonce.Generate()
};
}
}
public override string ToString()
{
StringBuilder header = new StringBuilder();
header.AppendFormat("realm=\"{0}\"", Realm);
header.AppendFormat(",nonce=\"{0}\"", Nonce);
header.AppendFormat(",qop=\"{0}\"", "auth");
return header.ToString();
}
}
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
public class Nonce
{
private static ConcurrentDictionary<string, Tuple<int, DateTime>>
nonces = new ConcurrentDictionary<string, Tuple<int, DateTime>>();
public static string Generate()
{
byte[] bytes = new byte[16];
using (var rngProvider = new RNGCryptoServiceProvider())
{
rngProvider.GetBytes(bytes);
}
string nonce = bytes.ToMD5Hash();
nonces.TryAdd(nonce, new Tuple<int, DateTime>(0, DateTime.Now.AddMinutes(10)));
return nonce;
}
public static bool IsValid(string nonce, string nonceCount)
{
Tuple<int, DateTime> cachedNonce = null;
//nonces.TryGetValue(nonce, out cachedNonce);
nonces.TryRemove(nonce, out cachedNonce);//每个nonce只允许使用一次
if (cachedNonce != null) // nonce is found
{
// nonce count is greater than the one in record
if (Int32.Parse(nonceCount) > cachedNonce.Item1)
{
// nonce has not expired yet
if (cachedNonce.Item2 > DateTime.Now)
{
// update the dictionary to reflect the nonce count just received in this request
//nonces[nonce] = new Tuple<int, DateTime>(Int32.Parse(nonceCount), cachedNonce.Item2);
// Every thing looks ok - server nonce is fresh and nonce count seems to be
// incremented. Does not look like replay.
return true;
}
}
}
return false;
}
}
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
[Authorize]
public class ProductsController : ApiController
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
GlobalConfiguration.Configuration.MessageHandlers.Add(
new AuthenticationHandler());
三、客户端的调用
这里主要说明使用WebClient调用
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
public static string Request(string sUrl, string sMethod, string sEntity, string sContentType,
out string sMessage)
{
try
{
sMessage = "";
using (System.Net.WebClient client = new System.Net.WebClient())
{
client.Credentials = CreateAuthenticateValue(sUrl);
client.Headers = CreateHeader(sContentType);
Uri url = new Uri(sUrl);
byte[] bytes = Encoding.UTF8.GetBytes(sEntity);
byte[] buffer;
switch (sMethod.ToUpper())
{
case "GET":
buffer = client.DownloadData(url);
break;
case "POST":
buffer = client.UploadData(url, "POST", bytes);
break;
default:
buffer = client.UploadData(url, "POST", bytes);
break;
}
return Encoding.UTF8.GetString(buffer);
}
}
catch (WebException ex)
{
sMessage = ex.Message;
var rsp = ex.Response as HttpWebResponse;
var httpStatusCode = rsp.StatusCode;
var authenticate = rsp.Headers.Get("WWW-Authenticate");
return "";
}
catch (Exception ex)
{
sMessage = ex.Message;
return "";
}
}
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
private static CredentialCache CreateAuthenticateValue(string sUrl)
{
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(new Uri(sUrl), "Digest", new NetworkCredential("Lime", "Lime"));
return credentialCache;
}
博客分类:
WebC#
webapiapiwebservice
摘要访问认证是一种协议规定的Web服务器用来同网页浏览器进行认证信息协商的方法。它在密码发出前,先对其应用哈希函数,这相对于HTTP基本认证发送明文而言,更安全。从技术上讲,摘要认证是使用随机数来阻止进行密码分析的MD5加密哈希函数应用。它使用HTTP协议。
一、摘要认证基本流程:
1.客户端请求 (无认证)
Html代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
GET /dir/index.html HTTP/1.0
Host: localhost
GET /dir/index.html HTTP/1.0 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代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
HTTP/1.0 401 Unauthorized
Server: HTTPd/0.9
Date: Sun, 10 Apr 2005 20:26:47 GMT
WWW-Authenticate: Digest realm="testrealm@host.com",
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
HTTP/1.0 401 Unauthorized Server: HTTPd/0.9 Date: Sun, 10 Apr 2005 20:26:47 GMT WWW-Authenticate: Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"
3.客户端请求 (用户名 "Mufasa", 密码 "Circle Of Life")
客户端接受到请求返回后,进行HASH运算,返回Authorization参数
其中:realm,nonce,qop由服务器产生;
uri:客户端想要访问的URI;
nc:“现时”计数器,这是一个16进制的数值,即客户端发送出请求的数量(包括当前这个请求),这些请求都使用了当前请求中这个“现时”值。例如,对一个给定的“现时”值,在响应的第一个请求中,客户端将发送“nc=00000001”。这个指示值的目的,是让服务器保持这个计数器的一个副本,以便检测重复的请求。如果这个相同的值看到了两次,则这个请求是重复的;
cnonce:这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护;
response:这是由用户代理软件计算出的一个字符串,以证明用户知道口令。
Html代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
<strong>response计算过程:</strong>
HA1=MD5(A1)=MD5(username:realm:password)
如果 qop 值为“auth”或未指定,那么 HA2 为
HA2=MD5(A2)=MD5(method:digestURI)
如果 qop 值为“auth-int”,那么 HA2 为
HA2=MD5(A2)=MD5(method:digestURI:MD5(entityBody))
如果 qop 值为“auth”或“auth-int”,那么如下计算 response:
response=MD5(HA1:nonce:nonceCount:clientNonce:qop:HA2)
如果 qop 未指定,那么如下计算 response:
response=MD5(HA1:nonce:HA2)
<strong>response计算过程:</strong> HA1=MD5(A1)=MD5(username:realm:password) 如果 qop 值为“auth”或未指定,那么 HA2 为 HA2=MD5(A2)=MD5(method:digestURI) 如果 qop 值为“auth-int”,那么 HA2 为 HA2=MD5(A2)=MD5(method:digestURI:MD5(entityBody)) 如果 qop 值为“auth”或“auth-int”,那么如下计算 response: response=MD5(HA1:nonce:nonceCount:clientNonce:qop:HA2) 如果 qop 未指定,那么如下计算 response: response=MD5(HA1:nonce:HA2)
请求头:
Html代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
GET /dir/index.html HTTP/1.0 Host: localhost
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
4.服务器响应
当服务器接收到摘要响应,也要重新计算响应中各参数的值,并利用客户端提供的参数值,和服务器上存储的口令,进行比对。如果计算结果与收到的客户响应值是相同的,则客户已证明它知道口令,因而客户的身份验证通过。
Html代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
HTTP/1.0 200 OK
HTTP/1.0 200 OK
二、服务端验证
编写一个自定义消息处理器,需要通过System.Net.Http.DelegatingHandler进行派生,并重写SendAsync方法。
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
public class AuthenticationHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
HttpRequestHeaders headers = request.Headers;
if (headers.Authorization != null)
{
Header header = new Header(request.Headers.Authorization.Parameter, request.Method.Method);
if (Nonce.IsValid(header.Nonce, header.NounceCounter))
{
// Just assuming password is same as username for the purpose of illustration
string password = header.UserName;
string ha1 = String.Format("{0}:{1}:{2}", header.UserName, header.Realm, password).ToMD5Hash();
string ha2 = String.Format("{0}:{1}", header.Method, header.Uri).ToMD5Hash();
string computedResponse = String.Format("{0}:{1}:{2}:{3}:{4}:{5}",
ha1, header.Nonce, header.NounceCounter,header.Cnonce, "auth", ha2).ToMD5Hash();
if (String.CompareOrdinal(header.Response, computedResponse) == 0)
{
// digest computed matches the value sent by client in the response field.
// Looks like an authentic client! Create a principal.
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, header.UserName),
new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.Password)
};
ClaimsPrincipal principal = new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, "Digest") });
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
}
}
}
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Digest",Header.UnauthorizedResponseHeader.ToString()));
}
return response;
}
catch (Exception)
{
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Digest",Header.UnauthorizedResponseHeader.ToString()));
return response;
}
}
}
public class AuthenticationHandler : DelegatingHandler { protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { try { HttpRequestHeaders headers = request.Headers; if (headers.Authorization != null) { Header header = new Header(request.Headers.Authorization.Parameter, request.Method.Method); if (Nonce.IsValid(header.Nonce, header.NounceCounter)) { // Just assuming password is same as username for the purpose of illustration string password = header.UserName; string ha1 = String.Format("{0}:{1}:{2}", header.UserName, header.Realm, password).ToMD5Hash(); string ha2 = String.Format("{0}:{1}", header.Method, header.Uri).ToMD5Hash(); string computedResponse = String.Format("{0}:{1}:{2}:{3}:{4}:{5}", ha1, header.Nonce, header.NounceCounter,header.Cnonce, "auth", ha2).ToMD5Hash(); if (String.CompareOrdinal(header.Response, computedResponse) == 0) { // digest computed matches the value sent by client in the response field. // Looks like an authentic client! Create a principal. var claims = new List<Claim> { new Claim(ClaimTypes.Name, header.UserName), new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.Password) }; ClaimsPrincipal principal = new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, "Digest") }); Thread.CurrentPrincipal = principal; if (HttpContext.Current != null) HttpContext.Current.User = principal; } } } HttpResponseMessage response = await base.SendAsync(request, cancellationToken); if (response.StatusCode == HttpStatusCode.Unauthorized) { response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Digest",Header.UnauthorizedResponseHeader.ToString())); } return response; } catch (Exception) { var response = request.CreateResponse(HttpStatusCode.Unauthorized); response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("Digest",Header.UnauthorizedResponseHeader.ToString())); return response; } } }
Header类
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
public class Header
{
public Header() { }
public Header(string header, string method)
{
string keyValuePairs = header.Replace("\"", String.Empty);
foreach (string keyValuePair in keyValuePairs.Split(','))
{
int index = keyValuePair.IndexOf("=", System.StringComparison.Ordinal);
string key = keyValuePair.Substring(0, index);
string value = keyValuePair.Substring(index + 1);
switch (key)
{
case "username": this.UserName = value; break;
case "realm": this.Realm = value; break;
case "nonce": this.Nonce = value; break;
case "uri": this.Uri = value; break;
case "nc": this.NounceCounter = value; break;
case "cnonce": this.Cnonce = value; break;
case "response": this.Response = value; break;
case "method": this.Method = value; break;
}
}
if (String.IsNullOrEmpty(this.Method))
this.Method = method;
}
public string Cnonce { get; private set; }
public string Nonce { get; private set; }
public string Realm { get; private set; }
public string UserName { get; private set; }
public string Uri { get; private set; }
public string Response { get; private set; }
public string Method { get; private set; }
public string NounceCounter { get; private set; }
// This property is used by the handler to generate a
// nonce and get it ready to be packaged in the
// WWW-Authenticate header, as part of 401 response
public static Header UnauthorizedResponseHeader
{
get
{
return new Header()
{
Realm = "MyRealm",
Nonce = WebApiDemo.Nonce.Generate()
};
}
}
public override string ToString()
{
StringBuilder header = new StringBuilder();
header.AppendFormat("realm=\"{0}\"", Realm);
header.AppendFormat(",nonce=\"{0}\"", Nonce);
header.AppendFormat(",qop=\"{0}\"", "auth");
return header.ToString();
}
}
public class Header { public Header() { } public Header(string header, string method) { string keyValuePairs = header.Replace("\"", String.Empty); foreach (string keyValuePair in keyValuePairs.Split(',')) { int index = keyValuePair.IndexOf("=", System.StringComparison.Ordinal); string key = keyValuePair.Substring(0, index); string value = keyValuePair.Substring(index + 1); switch (key) { case "username": this.UserName = value; break; case "realm": this.Realm = value; break; case "nonce": this.Nonce = value; break; case "uri": this.Uri = value; break; case "nc": this.NounceCounter = value; break; case "cnonce": this.Cnonce = value; break; case "response": this.Response = value; break; case "method": this.Method = value; break; } } if (String.IsNullOrEmpty(this.Method)) this.Method = method; } public string Cnonce { get; private set; } public string Nonce { get; private set; } public string Realm { get; private set; } public string UserName { get; private set; } public string Uri { get; private set; } public string Response { get; private set; } public string Method { get; private set; } public string NounceCounter { get; private set; } // This property is used by the handler to generate a // nonce and get it ready to be packaged in the // WWW-Authenticate header, as part of 401 response public static Header UnauthorizedResponseHeader { get { return new Header() { Realm = "MyRealm", Nonce = WebApiDemo.Nonce.Generate() }; } } public override string ToString() { StringBuilder header = new StringBuilder(); header.AppendFormat("realm=\"{0}\"", Realm); header.AppendFormat(",nonce=\"{0}\"", Nonce); header.AppendFormat(",qop=\"{0}\"", "auth"); return header.ToString(); } }nonce类
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
public class Nonce
{
private static ConcurrentDictionary<string, Tuple<int, DateTime>>
nonces = new ConcurrentDictionary<string, Tuple<int, DateTime>>();
public static string Generate()
{
byte[] bytes = new byte[16];
using (var rngProvider = new RNGCryptoServiceProvider())
{
rngProvider.GetBytes(bytes);
}
string nonce = bytes.ToMD5Hash();
nonces.TryAdd(nonce, new Tuple<int, DateTime>(0, DateTime.Now.AddMinutes(10)));
return nonce;
}
public static bool IsValid(string nonce, string nonceCount)
{
Tuple<int, DateTime> cachedNonce = null;
//nonces.TryGetValue(nonce, out cachedNonce);
nonces.TryRemove(nonce, out cachedNonce);//每个nonce只允许使用一次
if (cachedNonce != null) // nonce is found
{
// nonce count is greater than the one in record
if (Int32.Parse(nonceCount) > cachedNonce.Item1)
{
// nonce has not expired yet
if (cachedNonce.Item2 > DateTime.Now)
{
// update the dictionary to reflect the nonce count just received in this request
//nonces[nonce] = new Tuple<int, DateTime>(Int32.Parse(nonceCount), cachedNonce.Item2);
// Every thing looks ok - server nonce is fresh and nonce count seems to be
// incremented. Does not look like replay.
return true;
}
}
}
return false;
}
}
public class Nonce { private static ConcurrentDictionary<string, Tuple<int, DateTime>> nonces = new ConcurrentDictionary<string, Tuple<int, DateTime>>(); public static string Generate() { byte[] bytes = new byte[16]; using (var rngProvider = new RNGCryptoServiceProvider()) { rngProvider.GetBytes(bytes); } string nonce = bytes.ToMD5Hash(); nonces.TryAdd(nonce, new Tuple<int, DateTime>(0, DateTime.Now.AddMinutes(10))); return nonce; } public static bool IsValid(string nonce, string nonceCount) { Tuple<int, DateTime> cachedNonce = null; //nonces.TryGetValue(nonce, out cachedNonce); nonces.TryRemove(nonce, out cachedNonce);//每个nonce只允许使用一次 if (cachedNonce != null) // nonce is found { // nonce count is greater than the one in record if (Int32.Parse(nonceCount) > cachedNonce.Item1) { // nonce has not expired yet if (cachedNonce.Item2 > DateTime.Now) { // update the dictionary to reflect the nonce count just received in this request //nonces[nonce] = new Tuple<int, DateTime>(Int32.Parse(nonceCount), cachedNonce.Item2); // Every thing looks ok - server nonce is fresh and nonce count seems to be // incremented. Does not look like replay. return true; } } } return false; } }需要使用摘要验证可在代码里添加Attribute [Authorize],如:
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
[Authorize]
public class ProductsController : ApiController
[Authorize] public class ProductsController : ApiController最后Global.asax里需注册下
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
GlobalConfiguration.Configuration.MessageHandlers.Add(
new AuthenticationHandler());
GlobalConfiguration.Configuration.MessageHandlers.Add( new AuthenticationHandler());
三、客户端的调用
这里主要说明使用WebClient调用
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
public static string Request(string sUrl, string sMethod, string sEntity, string sContentType,
out string sMessage)
{
try
{
sMessage = "";
using (System.Net.WebClient client = new System.Net.WebClient())
{
client.Credentials = CreateAuthenticateValue(sUrl);
client.Headers = CreateHeader(sContentType);
Uri url = new Uri(sUrl);
byte[] bytes = Encoding.UTF8.GetBytes(sEntity);
byte[] buffer;
switch (sMethod.ToUpper())
{
case "GET":
buffer = client.DownloadData(url);
break;
case "POST":
buffer = client.UploadData(url, "POST", bytes);
break;
default:
buffer = client.UploadData(url, "POST", bytes);
break;
}
return Encoding.UTF8.GetString(buffer);
}
}
catch (WebException ex)
{
sMessage = ex.Message;
var rsp = ex.Response as HttpWebResponse;
var httpStatusCode = rsp.StatusCode;
var authenticate = rsp.Headers.Get("WWW-Authenticate");
return "";
}
catch (Exception ex)
{
sMessage = ex.Message;
return "";
}
}
public static string Request(string sUrl, string sMethod, string sEntity, string sContentType, out string sMessage) { try { sMessage = ""; using (System.Net.WebClient client = new System.Net.WebClient()) { client.Credentials = CreateAuthenticateValue(sUrl); client.Headers = CreateHeader(sContentType); Uri url = new Uri(sUrl); byte[] bytes = Encoding.UTF8.GetBytes(sEntity); byte[] buffer; switch (sMethod.ToUpper()) { case "GET": buffer = client.DownloadData(url); break; case "POST": buffer = client.UploadData(url, "POST", bytes); break; default: buffer = client.UploadData(url, "POST", bytes); break; } return Encoding.UTF8.GetString(buffer); } } catch (WebException ex) { sMessage = ex.Message; var rsp = ex.Response as HttpWebResponse; var httpStatusCode = rsp.StatusCode; var authenticate = rsp.Headers.Get("WWW-Authenticate"); return ""; } catch (Exception ex) { sMessage = ex.Message; return ""; } }关键代码,在这里添加用户认证,使用NetworkCredential
C#代码
![](http://zrj-software.iteye.com/images/icon_star.png)
![](http://zrj-software.iteye.com/images/spinner.gif)
private static CredentialCache CreateAuthenticateValue(string sUrl)
{
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(new Uri(sUrl), "Digest", new NetworkCredential("Lime", "Lime"));
return credentialCache;
}
private static CredentialCache CreateAuthenticateValue(string sUrl) { CredentialCache credentialCache = new CredentialCache(); credentialCache.Add(new Uri(sUrl), "Digest", new NetworkCredential("Lime", "Lime")); return credentialCache; }至此整个认证就ok了。 http://zrj-software.iteye.com/blog/2163487
相关文章推荐
- 使用 acl 编写 UDP 网络程序(UDP 重传及可靠性机制)
- 基于HTTP 协议认证介绍与实现
- HttpURLConnection getResponseCode()方法阻塞
- 配置nginx负载TCP服务器
- HTTP 请求方式: GET和POST的比较
- JavaScript---网络编程(3)-Object、String、Array对象和prototype属性
- JavaScript---网络编程(3)-Object、String、Array对象和prototype属性
- 解决- schema_reference.4: Failed to read schema document 'http://www.springframe
- Edusoho 网络课堂安装教程
- 基于Linux的网络编程
- 网络编程的基本概念
- Android Http缓存数据处理
- 开源的49款Java 网络爬虫软件
- HTTP的请求方法OPTIONS
- 《Indy In Depth》中文版
- 安卓HTTP实例中post方法和get方法的区别
- ISCSI 用法及简单配置
- TCP滑动窗口机制
- Python 3.5 HTTP服务器端重定向测试脚本
- 网络获取数组信息,根据姓名按首字母排序 添加侧滑删除。