在开发ASP.NET MVC应用程序时,经常需要处理身份验证和授权的问题。本文将介绍如何创建一个简单的“CookieAware”WebClient类,该类能够在不重复身份验证的情况下,访问控制器中需要授权的多个动作。
假设正在使用一个集成了表单身份验证的ASP.NET MVC应用程序。现在,需要使用单个WebClient来验证并访问多个需要身份验证的控制器动作。
通过在WebClient对象中持久化Cookies,可以避免每次请求都需要进行身份验证。为了解决这个问题,需要创建一个自定义的WebClient类,该类将在“访问”期间持久化身份验证令牌。这可以通过创建一个继承自WebClient类的类并具有一个特定的容器来存储身份验证Cookie来实现。
以下是创建CookieAwareWebClient类的代码示例:
public class CookieAwareWebClient : WebClient
{
public CookieContainer CookieContainer { get; private set; }
public CookieAwareWebClient()
{
CookieContainer = new CookieContainer();
}
}
在当前状态下,这个类除了创建一个额外的CookieContainer属性外,实际上并没有做任何事情。需要确定一种方法来实际填充身份验证Cookie,并将其存储在WebClient中,以便通过客户端本身进行的任何请求。
以下是在CookieAwareWebClient类中添加代码的示例,以实现身份验证:
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
request.CookieContainer = CookieContainer;
return request;
}
上述代码将关联当前从WebClient发出的请求,并添加适当的Cookie以处理客户端发出的任何后续请求。
以下示例详细说明了如何在MVC环境中使用新创建的Cookie-Aware WebClient,允许当前用户(在这种情况下,是通过Xamarin构建的iOS应用程序中的用户)使用ASP.NET MVC中的标准AccountController进行身份验证,然后调用带有[Authorize]属性的Controller中的方法。
以下是希望通过iOS调用访问的Controller和Action的示例:
[Authorize]
public class SecureController : Controller
{
public ActionResult YourSecureMethod(string data)
{
return Content(String.Format("{0} was passed in.", data));
}
}
如所见,除了Controller级别的[Authorize]装饰之外,Controller或Action本身并没有什么特别的,这很好,因为实际上并不需要为此功能做任何特别的事情。
现在,在iOS应用程序中,将在WebClient的一个实例中进行两次调用(第一次进行身份验证,第二次进行实际调用):
using (var client = new CookieAwareWebClient())
{
var values = new NameValueCollection
{
{ "UserName", username },
{ "Password", password }
};
client.UploadValues(new Uri("http://www.yourdomain.com/Account/LogOn/"), "POST", values);
client.UploadString(new Uri("http://www.yourdomain.com/Secure/YourSecureMethod"), "POST", "Example Message");
}
这就是需要的全部内容,因为可以在WebClient的上下文中调用许多其他Controller动作。需要注意的是,身份验证值只会在using语句期间持续存在,任何进一步尝试访问应用程序都需要额外的身份验证。
这只是WebClient被扩展以帮助实现一个非常简单的要求的一个例子,以前的几个冗余调用被避免了。发现另一个非常有帮助的功能是为WebClient实现超时机制。
处理来自iOS设备的请求时,超时可能是必要的,因为大多数用户不希望无限期地等待他们不小心按下的大写锁定键被拒绝。因此,实现超时可以在特定时间后停止请求,并通知用户他们需要重试他们的请求。
需要做的所有事情就是向WebClient类添加一个整数属性:
private int? _timeout = null;
public int? Timeout
{
get { return _timeout; }
set { _timeout = value; }
}
以及一个方法来处理设置它:
public void SetTimeout(int timeout)
{
_timeout = timeout;
}
最后,需要在WebRequest被创建时实际使用它,所以可以在那里添加逻辑:
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
request.CookieContainer = CookieContainer;
if (_timeout.HasValue)
{
request.Timeout = _timeout.Value;
}
return request;
}
这样,现在只需要在声明WebClient后设置属性(如果需要的话):
using (var client = new CookieAwareWebClient())
{
client.SetTimeout(5 * 60 * 1000);
client.Headers[HttpRequestHeader.KeepAlive] = "true";
}