2017-07-24 40 views
0

我已經被帶到了我的第一個MVC和C#項目,所以我非常感謝任何指導。MVC - 反僞造令牌錯誤

我創建了一項新功能,用於檢查用戶是否在登錄時進行過安全培訓。如果用戶沒有進行安全培訓,則會將用戶導向到培訓頁面,他們只是同意/不同意規則。如果用戶同意,則登錄完成。如果用戶不同意,他/她將被註銷。

我的問題是,當我在培訓視圖中選擇同意/不同意按鈕時,我得到以下error 它應該將我路由到主頁或註銷用戶。

控制器

public ActionResult UserSecurityTraining(int ID, string returnUrl) 
    { 
     // check if user already has taken training (e.g., is UserInfoID in UserSecurityTrainings table) 
     var accountUser = db.UserSecurityTraining.Where(x => x.UserInfoID == ID).Count(); 
     // If user ID is not in UserSecurityTraining table... 
     if (accountUser == 0) 
    { 
     // prompt security training for user 
     return View("UserSecurityTraining"); 
    } 
    // If user in UserSecurityTraining table... 
    if (accountUser > 0) 
    { 
     return RedirectToLocal(returnUrl); 
    } 
    return View(); 
} 

[HttpPost] 
[AllowAnonymous] 
[ValidateAntiForgeryToken] 
public async Task<ActionResult> UserSecurityTrainingConfirm(FormCollection form, UserSecurityTraining model) 
{ 
    if (ModelState.IsValid) 
    { 
     if (form["accept"] != null) 
     { 
      try 
      { 
       // if success button selected 
       //UserSecurityTraining user = db.UserSecurityTraining.Find(); //Create model object 
       //var user = new UserSecurityTraining { ID = 1, UserInfoID = 1, CreatedDt = 1 }; 

       logger.Info("User has successfully completed training" + model.UserInfoID); 
       model.CreatedDt = DateTime.Now; 
       db.SaveChanges(); 
       //return RedirectToAction("ChangePassword", "Manage"); 
      } 
      catch (Exception e) 
      { 
       throw e; 
      } 
      return View("SecurityTrainingSuccess"); 
     } 
     if(form["reject"] != null) 
     { 
      return RedirectToAction("Logoff", "Account"); 
     } 
    } 
    return View("UserSecurityTraining"); 
} 

查看

@model ECHO.Models.UserSecurityTraining 
@{ 
    ViewBag.Title = "Security Training"; 
    Layout = "~/Views/Shared/_LayoutNoSidebar.cshtml"; 
} 

<!--<script src="~/Scripts/RequestAccess.js"></script>--> 
<div class="container body-content"> 
    <h2>@ViewBag.Title</h2> 
    <div class="row"> 
     <div class="col-md-8"> 
      @using (Html.BeginForm("UserSecurityTrainingConfirm", "Account", FormMethod.Post, new { role = "form" })) 
      { 
       <fieldset> 
        @Html.AntiForgeryToken() 
        Please view the following security training slides:<br><br> 
        [INSERT LINK TO SLIDES]<br><br> 
        Do you attest that you viewed, understood, and promise to follow the guidelines outlined in the security training?<br><br> 
        <input type="submit" id="accept" class="btn btn-default" value="Accept" /> 
        <input type="submit" id="reject" class="btn btn-default" value="Reject" /> 

       </fieldset> 
      }     
     </div><!--end col-md-8--> 
    </div><!--end row--> 
</div><!-- end container --> 

@section Scripts { 
    @Scripts.Render("~/bundles/jqueryval") 
} 
+0

除非你返回一個無效模型,否則你應該只從HTTP POST控制器操作方法中返回'RedirectToAction'。例如,爲了保持適當的PRG(Post,Redirect,Get)模式,此行返回View(「SecurityTrainingSuccess」);'應該在這一行返回RedirectToAction(「SecurityTrainingSuccess」);'' MVC很大程度上依賴於你正確地遵循PRG,或者事情變得非常時髦。 – Tommy

回答

1

我不認爲你已經提供了足夠的代碼,以正確地診斷此特定錯誤的。一般來說,這種防僞造例外是由於認證狀態的改變。當您在頁面上調用@Html.AntiForgeryToken()時,還會在使用令牌的響應中設置cookie。重要的是,如果用戶通過了身份驗證,那麼用戶的身份將用於組成該令牌。然後,如果該用戶的身份驗證狀態在設置cookie之後和將表單發佈到驗證該令牌的操作之間發生變化,則該令牌將不再匹配。這可以是用戶在cookie設置後進行身份驗證,或者在cookie被設置後註銷。換句話說,如果用戶在頁面加載時是匿名的,但在提交表單之前已登錄,那麼它仍然會失敗。

再一次,我沒有看到任何代碼,這顯然會導致這種情況,但我們也沒有完整的圖片在用戶如何登錄到這個視圖在第一名。

這就是說,有一些非常明確的錯誤可能會導致這個問題,也可能不會導致這個問題,但肯定會在某個時候引起問題。首先,你的按鈕沒有name屬性。根據您的操作代碼,看起來好像您認爲id屬性將出現在您的FormCollection中,但情況並非如此。您需要分別將name="accept"name="reject"添加到當前代碼的按鈕中才能運行。

其次,對於用戶成功接受,您應該重定向到加載SecurityTrainingSuccess視圖的操作,而不是直接返回該視圖。 PRG(Post-Redirect-Get)模式的這一部分並確保提交不被重播。任何和所有的帖子操作都應該重定向成功。

第三,至少開箱即用,LogOff將會是一個post操作,這意味着你不能重定向到它。重定向總是通過GET。您可以從技術上使LogOff對GET或POST而不是POST做出響應,但這是一種反模式。原子動作應始終由POST(或更適當的動詞,如PUT,DELETE等)處理,但絕不會GET。

最後,雖然很小,但通常不鼓勵使用FormCollection。對於一個簡單的形式就是這樣,你可以從字面上只是你的綁定後如PARAMS:

public ActionResult UserSecurityTrainingConfirm(string accept, string reject, ...) 

但是,那麼它很可能是更符合邏輯,萬無一失引進一個布爾值,如:

public ActionResult UserSecurityTrainingConfirm(bool accepted, ...) 

然後,您的按鈕可以簡單地爲:

<button type="submit" name="accepted" value="true" class="btn btn-default">Accept</button> 
    <button type="submit" name="accepted" value="false" class="btn btn-default">Reject</button> 

這基本上使它們像收音機。被點擊的那個提交它的值,所以accepted參數將相應地爲真或假。另外,請注意,我已將您切換爲True button元素。對按鈕使用input是一種不好的做法,尤其是當您實際需要它提交值時,因爲值和顯示內在聯繫在一起。使用button元素,您可以發佈任何您想要的內容,並且仍然可以獨立標記您想要的任何文本。