在前一篇文章中,主要讨论了使用HTTP基本认证的方法,由于HTTP基本认证的方式决定了它在安全性方面存在很大的问题,因此接下来看看另外一种验证的方式:digest authentication,即摘要认证。api
在基本认证的方式中,主要的安全问题来自于用户信息的明文传输,而在摘要认证中,主要经过一些手段避免了此问题,大大增长了安全性。安全
下图为摘要验证的验证原理流程图。async
下面大体看一下这部分的验证流程:ide
摘要验证主要就是经过上面的HASH比较的步骤避免掉了基本验证中的安全性问题。this
须要注意的是,若是须要IIS支持摘要验证,须要把IIS摘要验证的特性勾上。spa
在理解了摘要验证的原理以后,只须要用代码实现便可。code
判断nonce是否过时的方法。orm
public static bool IsValid(string nonce, string nonceCount) { Tuple<int, DateTime> cachedNonce = null; nonces.TryGetValue(nonce, out cachedNonce); 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; } 判断nonce是否过时的代码
下面为摘要验证明现的核心方法server
namespace DigestAuthentication { public class AuthenticationHandler : DelegatingHandler { protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { try { var 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) }; var principal = new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, "Digest") }); Thread.CurrentPrincipal = principal; if (HttpContext.Current != null) HttpContext.Current.User = principal; } } } var 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; } } } } 摘要验证明现的核心方法
实现完成后,使用摘要验证只须要在对应的方法加上[Authorize]属性标签便可。blog
摘要验证很好地解决了使用基本验证所担忧的安全性问题。
可是永远没有绝对的安全,当用户使用字典进行穷举破解时,仍是会存在一些被破解的隐患。