我继承了System.Web.Http.AuthorizeAttribute来创建自定义授权/身份验证例程,以满足使用 ASP.NET MVC 4 开发的 Web 应用程序的一些不寻常的要求。这增加了安全性用于从 Web 客户端进行 Ajax 调用的 Web API。要求是:
- 用户每次执行交易时都必须登录以进行验证 在有人到达工作站后,其他人还没有走到工作站 登录并离开。
- 无法在程序时将角色分配给 Web 服务方法。 它们必须在运行时分配,以便管理员可以 配置这个。该信息存储在系统数据库中。
网络客户端是 single page application (SPA)因此,典型的表单例份验证效果不太好,但我正在尝试尽可能多地重用 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 header 中传递加密凭据。所以我的 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 header 检索凭据。然后,我使用 Membership.ValidateUser 来验证凭据。我插入了一个自定义成员(member)资格提供程序和角色提供程序来访问自定义数据库。如果用户通过身份验证,我就会检索资源和操作的角色。从那里我使用基本的 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 状态代码,并且它可以做出相应的响应。
那么在这种情况下,为当前原则设置IsAuthenticated的正确方法是什么?
最佳答案
适合我的场景的最佳解决方案似乎是完全绕过基本的OnAuthorization。由于我每次都必须进行身份验证,因此 cookie 和缓存的原理没有多大用处。所以这是我想出的解决方案:
public override void OnAuthorization(HttpActionContext actionContext)
{
string username;
string password;
if (GetUserNameAndPassword(actionContext, out username, out password))
{
if (Membership.ValidateUser(username, password))
{
if (!isUserAuthorized(username))
actionContext.Response =
new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
}
else
{
actionContext.Response =
new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
}
}
else
{
actionContext.Response =
new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
}
}
我开发了自己的方法来验证名为 isUserAuthorized 的角色,并且我不再使用基本 OnAuthorization ,因为它会检查当前的原则> 查看它是否经过身份验证。 IsAuthenticated 只允许获取,所以我不确定如何设置它,而且我似乎不需要当前的原则。测试了一下,效果很好。
如果有人有更好的解决方案或者可以看到这个问题,仍然感兴趣。
关于asp.net - 如何自定义 ASP.NET Web API AuthorizeAttribute 以满足异常需求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12629530/