我从System.Web.Http.AuthorizeAttribute继承以创建自定义授权/身份验证 routine ,以满足使用ASP.NET MVC 4开发的Web应用程序的一些特殊要求.这为Web客户端的AJAX调用使用的Web API增加了安全性.要求是:
- 用户必须在每次执行事务时登录以进行验证
- 不能在编程时将角色分配给Web服务方法. 必须在运行时分配它们,以便管理员可以 对此进行配置.此信息存储在系统数据库中.
Web客户端是single page application (SPA),因此典型的窗体身份验证不能很好地工作,但我正在try 尽可能多地重用ASP.NET安全框架以满足要求.定制的AuthorizeAttribute在确定哪些角色与Web服务方法相关联方面非常适合需求2.我接受三个参数(应用程序名称、资源名称和操作)来确定哪些角色与方法相关联.
public class DoThisController : ApiController
{
[Authorize(Application = "MyApp", Resource = "DoThis", Operation = "read")]
public string GetData()
{
return "We did this.";
}
}
我重写OnAuthorization方法来获取角色并验证用户.由于每个事务都必须对用户进行身份验证,因此我通过在同一步骤中执行身份验证和授权来减少来回的喋喋不休.我通过使用基本身份验证从web客户端获取用户凭据,该身份验证在HTTP头中传递加密的凭据.所以我的OnAuthorization方法是这样的:
public override void OnAuthorization(HttpActionContext actionContext)
{
string username;
string password;
if (GetUserNameAndPassword(actionContext, out username, out password))
{
if (Membership.ValidateUser(username, password))
{
FormsAuthentication.SetAuthCookie(username, false);
base.Roles = GetResourceOperationRoles();
}
else
{
FormsAuthentication.SignOut();
base.Roles = "";
}
}
else
{
FormsAuthentication.SignOut();
base.Roles = "";
}
base.OnAuthorization(actionContext);
}
GetUserNameAndPassword从HTTP头中检索凭据.然后我使用Membership.ValidateUser验证凭证.我有一个自定义的成员资格提供程序和角色提供程序插入到一个自定义数据库.如果用户经过身份验证,那么我将检索资源和操作的角色.在此基础上,我使用base OnAuthorization完成授权过程.这就是它的故障所在.
如果用户通过了身份验证,我将使用标准的窗体身份验证方法让用户登录(FormsAuthentication.SetAuthCookie),如果失败,我将他们注销(FormsAuthentication.SignOut).但问题似乎是,基数OnAuthorization类无法访问更新后的Principal,因此IsAuthenticated被设置为正确的值.它总是落后一步.我的猜测是,它使用的是一些缓存值,这些值在到Web客户端的往返行程之前不会更新.
所有这些都引出了我的具体问题,那就是,有没有其他方法可以在不使用cookie的情况下将IsAuthenticated设置为当前Principal的正确值?在我看来,Cookie并不真正适用于我每次都必须进行身份验证的特定场景.我知道IsAuthenticated没有设置为正确值的原因是我还将HandleUnauthorizedRequest方法覆盖为:
protected override void HandleUnauthorizedRequest(HttpActionContext filterContext)
{
if (((System.Web.HttpContext.Current.User).Identity).IsAuthenticated)
{
filterContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
如果失败是因为授权而不是身份验证,这允许我向web客户端返回禁止的状态代码,并且它可以相应地响应.
那么,在这种情况下,为当前的Principle设置IsAuthenticated的正确方法是什么?