【ASP.NET Web API2】Digest认证
Digest摘要认证
对于Basic认证方案来说,被传输的认证凭证仅仅采用Base64编码,所以包含密码的认证凭证基本上可以视为明文传输。Digest认证在此基础上进行了改善,在一定程度上提高了安全系数。
在了解Digest认证的工作原理之前,我们看看通过浏览器调用一个Digest认证的Web API会涉及怎样的消息交换
采用浏览器作为Web API调用的客户端,与Basic认证方式一样,浏览器会针对Web API 的第一次调用 会自动弹出登陆对话框
正确输入账号密码(Web API 配置文件中定义)后,输出结果会呈现在浏览器中。利用Fiddler我们会发现整个调用涉及两次消息交换

GET http://localhost:4003/api/values HTTP/1.1
Host: localhost:4003
Connection: keep-alive
Cache-Control: max-age=0
Authorization: Digest username="", realm="UZ.COM", nonce="1b4d156b1af16f4ea72a040396d27f86", uri="/api/values", response="07e5a5012586dbffbad8040a923fb11a", qop=auth, nc=00000001, cnonce="4aee7270082bcd10"
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 HTTP/1.1 401 Unauthorized
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
WWW-Authenticate: Digest realm="UZ.COM",nonce="4c4c15480802fe3bccb45605f1f4126d",qop="auth"
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RTpcVGVzdFxEaWdlc3REZW1vXERpZ2VzdERlbW9cYXBpXHZhbHVlcw==?=
X-Powered-By: ASP.NET
Date: Fri, 12 Jun 2015 08:16:02 GMT
Content-Length: 81 <Error><Message>Authorization has been denied for this request.</Message></Error>

GET http://localhost:4003/api/values HTTP/1.1
Host: localhost:4003
Connection: keep-alive
Cache-Control: max-age=0
Authorization: Digest username="loding", realm="UZ.COM", nonce="4c4c15480802fe3bccb45605f1f4126d", uri="/api/values", response="9495b8ef2b5b1d6dd3001753293cca57", qop=auth, nc=00000001, cnonce="2dfe2e5c0b8a4000"
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RTpcVGVzdFxEaWdlc3REZW1vXERpZ2VzdERlbW9cYXBpXHZhbHVlcw==?=
X-Powered-By: ASP.NET
Date: Fri, 12 Jun 2015 08:17:21 GMT
Content-Length: 195 <ArrayOfstring xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays"><string>value1</string><string>value2</string></ArrayOfstring>
验证流程:
1,客户端以匿名的方式发送一个请求
2,服务器返回状态为"401 Unauthorized",这个响应含有"WWW-Authenticate"报头
- Digest:表示当前的认证方式。
- realm:领域
- nonce:服务器生成的具有唯一性的字符串
- qop(Quanlity of Protection):IIS采用qop通知客户端采用的消息保护等级。可选的qop包括"auth"和"auth-int"。
前者表示仅限于基本的认证,后者则表示除了对客户端实施认证外,还需要确保传输内容的一致性。
3,客户端接收到返回的401状态后,会自动弹出一个登陆对话框
4,得到用户输入的用户名和密码,浏览器根据密码和服务端生成的nonce采用一种算法生成一个Digest,这个Digest会作为认证凭证放 到"Authorization"报头response中,随后向服务器发送请求
其他信息:
- username:代表客户端用户名
- nonce:就是服务器生成返回的那个nonce
- cnonce:这是客户端自行生成的nonce,它可以对请求内容进行签名以确保内容没有被篡改,同时也可以帮助客户端对服务器实施认证
- nc(nonce count):表示客户端针对同一个nonce发送请求的数量,随着请求的发送而不断递增。
IIS可以通过nc代表的数字来防止“重发攻击”。
5,服务器接收到请求后进行nc合法性验证等,然后确定客户端提供密码的正确性,完成对客户端的认证
Digest认证实现(源码下载)
https://coding.net/u/Allen_/p/ASP.NET-Web-API-2---Digest/git
验证实现的核心方法:
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))
{
string uzkey = ConfigurationManager.AppSettings["UZKey"];
string uzsecret = ConfigurationManager.AppSettings["UZSecret"]; string ha1 = HashHelper.GetMD5(String.Format("{0}:{1}:{2}", uzkey, header.Realm, uzsecret)); string ha2 = HashHelper.GetMD5(String.Format("{0}:{1}", header.Method, header.Uri)); string computedResponse = HashHelper.GetMD5(String.Format("{0}:{1}:{2}:{3}:{4}:{5}",
ha1, header.Nonce, header.NounceCounter, header.Cnonce, "auth", ha2)); 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;
}
}
}
使用Digest认证还需要在全局配置文件中注册并在Controller中加[Authorize]属性标签

使用WebClient调用Web API
private 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);
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 "";
}
} private static CredentialCache CreateAuthenticateValue(string sUrl)
{
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(new Uri(sUrl), "Digest", new NetworkCredential("用户名", "密码")); return credentialCache;
}
【ASP.NET Web API2】Digest认证的更多相关文章
- [水煮 ASP.NET Web API2 方法论](3-9)空气路由的设置
阅读导航 问题 解决方案 工作原理 代码演示 在此解释一下,空气路由,是本人臆想出来,觉着更能表达 IgnoreRoute 的意图,如果看着辣眼睛^^,请见谅. 问题 我们在之定义过集中式路由,集中式 ...
- asp.net web api2.0 ajax跨域解决方案
asp.net web api2.0 ajax跨域解决方案 Web Api的优缺点就不说了,直接说怎么跨域,我搜了一下,主要是有两种. 一,ASP.NET Web API支持JSONP,分两种 1, ...
- ASP.NET Web API安全认证
http://www.cnblogs.com/codeon/p/6123863.html http://open.taobao.com/docs/doc.htm?spm=a219a.7629140.0 ...
- [水煮 ASP.NET Web API2 方法论](3-8)怎样给指定路由配置处理器
阅读导航 问题 解决方案 工作原理 代码演示 问题 如果仅仅针对指定的路由进行某些特定的消息处理,而不是应用于所有路由,我们应该怎么做呢? 解决方案 ASP.NET WEB API 的很多功能都内建了 ...
- [水煮 ASP.NET Web API2 方法论](1-5)ASP.NET Web API Scaffolding(模板)
问题 我们想快速启动一个 ASP.NET Web API 解决方案. 解决方案 APS.NET 模板一开始就支持 ASP.NET Web API.使用模板往我们的项目中添加 Controller,在我 ...
- [水煮 ASP.NET Web API2 方法论](3-7)默认 Action 请求方式以及 NonActionAttribute
问题 在 Controller 中有一个 public 的方法,但是又不想将这个 publlic 方法暴露成为一个 API. 解决方案 ASP.NET Web API 中,正常是通过 HTTP 谓词来 ...
- [水煮 ASP.NET Web API2 方法论](3-6)万能路由
问题 定义什么样的路由,可以不会受请求参数类型和数量的限制,而被全部捕获? 解决方案 在路由模板中,给参数添加一个"*"前缀,例如 {*param},只要请求的 URL 能够和路由 ...
- [水煮 ASP.NET Web API2 方法论](3-5)路由约束
问题 怎么样限制路由中参数的值. 解决方案 ASP.NET WEB API 允许我们通过 IHttpRouteConstraint 接口设置路由约束.集中式路由和直接式路由都可以使用 IHttpRou ...
- [水煮 ASP.NET Web API2 方法论](3-4)设置路由可选项
问题 怎么样创建一个路由,不管客户端传不传这个参数,都可以被成功匹配. 解决方案 ASP.NET WEB API 的集中式路由和属性路由都支持路由声明可选参数. 在用集中式路由中可以通过 RouteP ...
随机推荐
- 在 React Native 中使用 Redux 架构
前言 Redux 架构是 Flux 架构的一个变形,相对于 Flux,Redux 的复杂性相对较低,而且最为巧妙的是 React 应用可以看成由一个根组件连接着许多大大小小的组件的应用,Redux 也 ...
- windows技巧--一次关闭所有资源管理器目录,文件夹目录
每天开机工作一段时间以后,你可能会和我一样,打开了很多的文件目录,于是一个一个的点窗口关闭.于是想有没有一次关闭所有目录的办法~~咚咚咚,经过一番寻觅,下面是我找到的办法 新建bat文件 close_ ...
- Python 类的三大特性的综合运用 案例
# --------------------- 类的三大特性的综合运用 案例 ------------------------- # 定义三个类:小狗,小猫,人 # 小狗:姓名,年龄(默认1岁) 吃饭 ...
- linux 进阶命令___0001
查看指定目录下最大的文件 #查看/var目录下前10个最大的文件 #Find top 10 largest files in /var directory (subdirectories and hi ...
- HBase 协处理器编程详解,第二部分:客户端代码编写
实现 Client 端代码 HBase 提供了客户端 Java 包 org.apache.hadoop.hbase.client.coprocessor.它提供以下三种方法来调用协处理器提供的服务: ...
- eclipse——反编译插件
百度云链接 链接:https://pan.baidu.com/s/1iEtstiK5mJ4kDp6gTfVBww 密码:8wf7 在线安装地址 http://jd.benow.ca/jd-eclips ...
- NUnit单元测试笔记
vs2010 和 NUnit 问题处理. . 在 <configuration> 下 加 ... <startup> <requiredRuntime version=& ...
- Software Engineering at Google
Google的Fergus Henderson在Software Engineering at Google中介绍了Google的软件工程实践. 软件开发 源码仓库 单一源代码仓库,除了核心配置和安全 ...
- 更新CentOS 6.7源为阿里源
1.备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 2.下载新的CentOS-Base ...
- SQL优化之列裁剪和投影消除
列裁剪 对于没用到的列,则没有必要读取它们的数据去浪费无谓的IO 比如我们有一张表table1,它含有四列数据(a,b,c,d).当我们执行查询select a from table1 where c ...