ASP.NET MVC 4实现基本授权和身份验证层非常简单;它全部使用“ASP.NET MVC 4 Web应用程序”项目模板自动生成。
但是,我的任务是实现一些需要重新认证的控制器操作,我的目标是提供可维护的解决方案。简单地说一下用户故事,我正在尝试实现以下内容:
请注意,'reauthentication'不必改变当前用户会话的状态。
显然,有很多方法可以实现这一点,但我觉得看起来像下面(伪)样本类似的实现符合我的需要。
[Authorize]
[InitializeSimpleMembership]
public class SpecialActionController : Controller
{
public ActionResult SpecialForm() { return View(); }
public ActionResult Succes() { return View(); }
[HttpPost]
[ReAuthenticate] /* <- Prompts user with reauthentication form before proceeding. */
public ActionResult SpecialForm(SpecialFormModel model)
{
if (ModelState.IsValid)
RedirectToAction("Succes");
else
return View(model);
}
}
有什么建议吗?
编辑:我忘了提及任何与OAuth相关的功能都超出了范围。外部身份验证不是问题,不需要支持。事实上,在我正在开发的当前项目中,所有与OAuth相关的功能都将被删除或停用。
答案 0 :(得分:1)
您应该可以使用自定义AuthorizeAttribute和Session
的组合来完成此操作。覆盖AuthorizeCore方法并允许进行所有默认身份验证,但引入您自己的额外检查(用于重新身份验证),例如。
public class RecurringAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var reauth = (bool?)httpContext.Session["ReAuthenticated"];
var result = base.AuthorizeCore(httpContext) && (reauth ?? false);
httpContext.Session["ReAuthenticated"] = !result;
return result;
}
}
这应该在用户每次点击操作时将用户重定向到登录页面,并且他们没有重新进行身份验证。如果用户已重新进行身份验证,我们会清除会话变量以强制登录下一个请求。
为了使其正常工作,我们需要一个钩子来设置ReAuthentication
会话变量 - 我认为LogOn
中的AccountController
方法将是此
public class AccountController : Controller
{
...
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
Session["ReAuthenticated"] = User.Identity.IsAuthenticated;
return RedirectToLocal(returnUrl);
}
...
}
}
然后剩下要做的就是装饰我们的控制器动作
[Authorize]
public ActionResult SomePrivateAction()
{
...
}
[RecurringAuthorize]
public ActionResult SomeSuperSecretAction()
{
...
}
对于使用默认AuthorizeAttribute
的任何操作,您应该会发现授权将正常工作,并且每次1>}的任何操作都会被强制登录他们请求页面,其中包括页面刷新。
答案 1 :(得分:0)
我试图实现假设的[ReAuthenticate] - 属性,但发现自己过于依赖反射。在考虑了一个更易于管理的解决方案之后,我终于想出了以下内容:
ReAuth类
public sealed class ReAuth
{
#region Constructor
private ReAuth(Func<System.Web.Mvc.ActionResult> onSuccessAction)
{
this.onSuccessAction = onSuccessAction;
}
#endregion
#region Public static members
public static ReAuth CreateFor(HttpSessionStateBase httpSession, Func<System.Web.Mvc.ActionResult> onSuccessAction)
{
httpSession[sessionKey] = new ReAuth(onSuccessAction);
return GetFor(httpSession);
}
public static ReAuth GetFor(HttpSessionStateBase httpSession)
{
return httpSession[sessionKey] as ReAuth;
}
public static bool ExistsFor(HttpSessionStateBase httpSession)
{
return httpSession[sessionKey] as ReAuth != null;
}
#endregion
#region Public instance members
public bool ReAuthenticated { get; set; }
public System.Web.Mvc.ActionResult Handle()
{
if (ReAuthenticated)
return onSuccessAction();
else
return new System.Web.Mvc.RedirectToRouteResult(
new System.Web.Routing.RouteValueDictionary
{
{ "Controller", "#" }, /* Replace '#' with the name of the controller that implements the re-authentication form... */
{ "Action", "#" } /* Replace '#' with the name of the action on the aforementioned controller. */
});
}
#endregion
#region Private members
private const string sessionKey = "reAuthenticationSessionKey";
private readonly Func<System.Web.Mvc.ActionResult> onSuccessAction;
#endregion
}
<强>实施强>
假设我们有一个假设的控制器,其中应用了解决方案:
public class AccountInfoController : System.Web.Mvc.Controller
{
/* snip... */
[HttpPost]
public ActionResult EditAccountInfo(AccountInfo model)
{
if (ModelState.IsValid)
return ReAuth.CreateFor(Session, () => { return Success(); }).Handle();
else
return View(model);
}
}
...而且,我们需要一个控制器(实质上是真正的 AccountController的副本,它不会篡改表单身份验证用户会话状态),其中重新认证发生。:
public class ReAuthController : System.Web.Mvc.Controller
{
/* Snip... */
[HttpPost]
public ActionResult LogOn(LogOnModel model)
{
if (ModelState.IsValid)
{
ReAuth.GetFor(Session).ReAuthenticated = Membership.ValidateUser(model.User, model.Password);
return ReAuth.Handle();
}
return View(model);
}
}
据我所知,这是一个可管理的解决方案。它确实依赖于将对象存储到会话状态。 (特别是实现ReAuth类的控制器的对象状态)如果有人有其他建议,请告诉我!