2014-02-06 116 views
0

使用ASP.NET MVC 4實現基本授權和身份驗證層非常簡單;它都是使用「ASP.NET MVC 4 Web應用程序」項目模板自動生成的。在ASP.NET MVC 4中實現'ReAuthentication'屬性?

但是,我的任務是實施一些需要重新驗證的控制器操作,並且我的目標是提供可維護的解決方案。簡單地說,我試圖執行以下操作:

  1. 用戶登錄;
  2. 用戶導航到呈現窗體視圖的控制器(歸因於[Authorize])操作;
  3. 用戶通過提交表單來執行POST;
  4. 會出現一個身份驗證表單,用戶需要使用他/她的用戶名和密碼重新進行身份驗證;
  5. 如果認證成功,繼續處理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); 
    } 

} 

有什麼建議嗎?

編輯:我忘了提及任何與OAuth相關的功能超出範圍。外部認證在這裏不是問題,不需要支持。事實上,在我正在開展的當前項目中,所有與OAuth相關的功能都將被刪除或取消激活。

+0

讓某人*重新認證*看起來很奇怪,當他們已經通過認證時,您是否正在尋找類似於UAC的東西?我猜想它增加了另一層安全性,但我想可能會讓用戶感到有點沮喪。你不能用角色來代替嗎? – James

+0

你好詹姆斯。您對某個解決方案是否適合_user friendly很有幫助;我真的懷疑我是否會自己決定這樣的事情。不幸的是,這些規範決定了這個功能,客戶想要它,所以沒有辦法解決這個問題。另外,不幸的是,角色的使用不會產生影響,因爲沒有任何影響。不過謝謝你的想法! –

+1

這絕對是可行的,我認爲棘手的部分將不會影響當前的身份驗證狀態。您是否只需重新認證一次,或者每次都需要重新認證? – James

回答

0

我試圖實現假設的[ReAuthenticate] -attribute,但發現自己太依賴於反射。把一些思想到一個更易於管理的解決方案後,我終於想出了以下內容:

重新驗證類

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類的控制器的對象狀態)如果有人有其他建議,請告訴我!

+0

我很高興你設法找到適合你的解決方案,但是,對我來說,這裏有點太多了。就像我在之前的評論中提到的那樣,這可以通過使用自定義屬性來實現,在我看來,這是一種更簡潔的解決方案 - 請參閱我的答案。 – James

1

您應該可以使用自定義AuthorizeAttributeSession的組合執行此操作。覆蓋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會話變量 - 我認爲在AccountControllerLogOn方法將是本

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任何動作正常,並飾以任何行動將被迫每次登錄他們要求頁面,其中包括頁面刷新。

+0

你好詹姆斯,謝謝你的解決方案。與我的相比,我肯定能看到這方面的好處;特別是涉及的代碼量。但我確實看到了一個問題:如果它是[HttpPost]標記的模型接受操作,那麼執行'SomeSuperSecretAction'會發生什麼?它會丟失或必須通過另一個Session變量重新獲得,對吧?據我所知,對於「您必須重新進行身份驗證才能查看此表單」重新認證方式,但對於「在我們處理您的輸入之前重新進行身份驗證」 - 重新認證方式而言,這非常好。 –

+0

@RobWijkstra這不起作用(至少開箱即可)。問題是當你嘗試「POST」時,用戶被重定向到登錄頁面 - 此時POST數據丟失。有辦法解決這個問題,然而,你需要做一些自定義的事情,最有可能利用'Session'來存儲POST數據,或者使用[307狀態碼](http://programmers.stackexchange.com/問題/ 99894/why-doesnt-http-have-post-redirect)在重定向期間保留POST數據。 – James