Asp.net mvc 实施';重新认证';ASP.NET MVC 4中的属性?

Asp.net mvc 实施';重新认证';ASP.NET MVC 4中的属性?,asp.net-mvc,asp.net-mvc-4,Asp.net Mvc,Asp.net Mvc 4,使用ASP.NET MVC 4实现基本的授权和身份验证层非常容易;所有这些都是使用“ASP.NET MVC 4 Web应用程序”项目模板自动生成的 然而,我的任务是实现一些需要重新验证的控制器操作,我的目标是一个可维护的解决方案。简单地说,在一个用户故事中,我尝试实现以下内容: 用户登录 用户导航到呈现表单视图的控制器(属性为[Authorize])操作 用户通过提交表单执行POST 出现一个身份验证表单,其中用户需要使用其用户名和密码重新进行身份验证 如果身份验证成功,请继续处理POST请求

使用ASP.NET MVC 4实现基本的授权和身份验证层非常容易;所有这些都是使用“ASP.NET MVC 4 Web应用程序”项目模板自动生成的

然而,我的任务是实现一些需要重新验证的控制器操作,我的目标是一个可维护的解决方案。简单地说,在一个用户故事中,我尝试实现以下内容:

  • 用户登录
  • 用户导航到呈现表单视图的控制器(属性为[Authorize])操作
  • 用户通过提交表单执行POST
  • 出现一个身份验证表单,其中用户需要使用其用户名和密码重新进行身份验证
  • 如果身份验证成功,请继续处理POST请求
  • 请注意,“重新验证”不必改变当前用户会话的状态

    显然,有很多方法可以实现这一点,但是我觉得一个看起来类似于以下(伪)示例的实现可以满足我的需要

    [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);
        }
    
    }
    
    [授权]
    [姓名首字母缩写SampleMembership]
    公共类SpecialActionController:控制器
    {
    public ActionResult SpecialForm(){return View();}
    public ActionResult success(){return View();}
    [HttpPost]
    
    [ReAuthenticate]/*我试图实现假设的[ReAuthenticate]-属性,但发现自己过于依赖反射。在考虑了一个更易于管理的解决方案后,我最终得出以下结论:

    重新授权类

    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
    }
    
    …而且,我们需要一个控制器(本质上是真实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类的控制器的对象状态)如果有任何人有其他建议,请告诉我!

    您应该能够使用自定义和
    会话的组合来完成此操作。覆盖此方法并允许进行所有默认身份验证,但引入您自己的额外检查(用于重新身份验证),例如

    这将在用户每次点击操作且未重新身份验证时将其重新定向到登录页面。如果用户已重新身份验证,我们将清除会话变量以在下一个请求中强制登录

    要使其正常工作,我们需要一个钩子来设置
    重新验证
    会话变量-我认为
    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
    的任何操作,授权都将正常工作,并且使用
    RecurringAuthorizeAttribute
    修饰的任何操作在每次请求页面时都将被强制登录,其中包括页面刷新。

    当有人重新进行身份验证时,似乎有些奇怪ady authenticated,您是否希望实现类似于UAC的功能?我想这会增加另一层安全性,但我想这可能会让用户感到有点沮丧。您不能改用角色吗?您好,James。您对某个解决方案是否对用户友好提出了很好的看法;我真的怀疑这是什么原因不管是不是这样,我都会自己决定。不幸的是,规范规定了这项功能,而客户需要它,因此没有办法摆脱它。同样不幸的是,角色的使用不会产生影响,因为没有任何影响。不过,感谢您的想法!这是绝对可行的,我认为棘手的部分将是不影响当前身份验证状态。您是只需要重新身份验证一次还是每次都需要重新身份验证?每次都需要重新身份验证。您是否有任何有用的资源?是否可以从中派生一个基本属性类来帮助我进行验证?是的,您很可能希望从中删除新属性。但是,当OP已通过身份验证时,您将遇到的问题是强制重新身份验证,我假设内部将检查存储的身份验证cookie。我正在编写一个答案,但它主要是推测性的,因为我没有具体的证据证明它会起作用,我将有一个思考abo我很高兴你找到了一个适合你的解决方案,但是,对我来说,这里有点太多。正如我在前面的评论中提到的,这可以通过使用自定义属性来实现,在我看来,这是一个更简洁的解决方案-请看我的答案。你好,詹姆斯,谢谢你的解决方案。我可以确定与我的相比,我看到了这一点的好处;特别是涉及的代码量。但我确实看到了一个问题:如果是[HttpPost]标记的模型接受操作,“SomeSuperSecretAction”的执行会发生什么情况?它将丢失或必须通过另一个会话变量重新获得,对吗?据我所知,它对我们来说非常有用“您必须重新验证才能查看此表单”类型的重新验证,但不是“在我们处理您的输入之前重新验证”-类型的重新验证。@RobWijkstra这行不通(至少开箱即用)。问题是当您尝试
    发布
    时,用户将被重定向到登录页面-此时,发布数据将丢失。但是,有一些方法可以解决此问题,您需要进行自定义操作,并且很可能使用
    会话
    来存储发布数据,或者在发布期间使用来保留发布数据我们直接。
    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()
    {
       ...
    }