2017-07-24 84 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元素,您可以发布任何您想要的内容,并且仍然可以独立标记您想要的任何文本。