我从System.Web.Http.AuthorizeAttribute继承来创建自定义授权/身份验证例程,以满足使用 ASP.NET MVC 4 开发的 Web 应用程序的一些不寻常的要求。这增加了用于来自 Web 的 Ajax 调用的 Web API 的安全性客户。要求是:
- 用户每次执行交易时都必须登录,以验证在某人登录并离开后其他人没有走到工作站。
- 不能在程序时将角色分配给 Web 服务方法。必须在运行时分配它们,以便管理员可以配置它。此信息存储在系统数据库中。
Web 客户端是单页应用程序 (SPA),因此典型的表单身份验证不能很好地工作,但我正在尝试尽可能多地重用 ASP.NET 安全框架来满足要求。定制的AuthorizeAttribute非常适合要求 2 确定哪些角色与 Web 服务方法相关联。我接受三个参数,应用程序名称、资源名称和操作来确定哪些角色与方法相关联。
public class DoThisController : ApiController
{
[Authorize(Application = "MyApp", Resource = "DoThis", Operation = "read")]
public string GetData()
{
return "We did this.";
}
}
我重写OnAuthorization方法以获取角色并对用户进行身份验证。由于每个事务都必须对用户进行身份验证,因此我通过在同一步骤中执行身份验证和授权来减少来回的喋喋不休。我通过使用在 HTTP 标头中传递加密凭据的基本身份验证从 Web 客户端获取用户凭据。所以我的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来验证凭据。我有一个自定义成员资格提供程序和角色提供程序插入以访问自定义数据库。如果用户已通过身份验证,我将检索资源和操作的角色。从那里我使用基础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 客户端返回 Forbidden 状态代码,并且它可以相应地响应。
那么在这种情况下,为当前Principle设置IsAuthenticated的正确方法是什么?