簡體   English   中英

MVC 5-在登錄時設置會話變量

[英]MVC 5 - set session variable at login

我正在構建MVC 5應用程序,並且遇到以下問題:用戶登錄后,如果用戶與我達成協議,我想向用戶顯示菜單項。

我想在用戶登錄時設置會話變量,例如:

Session["HasAgreement"] = Agreement.HasAgreement(userId);

然后在構建菜單的_Layout.cshtml文件中執行以下操作:

@if (Session["HasAgreement"] == "True")
{
   <li>@Html.ActionLink("Agreement", "Agreement", "Home")</li>
}

我的問題出在AccountController上,在其中我已將邏輯添加到標准Login Action中:

public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
    switch (result)
    {
        case SignInStatus.Success:
            var userId = User.Identity.GetUserId();
            Session["HasAgreement"] = Agreement.HasAgreement(userId);
            return RedirectToLocal(returnUrl);
        case SignInStatus.LockedOut:
            return View("Lockout");
        case SignInStatus.RequiresVerification:
            return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
        case SignInStatus.Failure:
        default:
            ModelState.AddModelError("", "Invalid login attempt.");
            return View(model);
    }
}

這是標准的MVC 5登錄-除了在“ case SignInStatus.Success:”之后添加兩行外,我嘗試獲取userId,然后設置Session變量。

我的問題是,此時在thime中,用戶尚未通過身份驗證(我認為是在上面的SignInManager中發生的)。

用戶登錄后如何設置會話變量?

在您執行下一個操作之前,不會設置新的會話。 嘗試將結果用於重定向以外的其他問題時,這是一個常見問題。 最好的措施是使用結果重定向到另一個操作,然后您就可以在其中訪問會話。

假設您的用戶登錄時轉到“儀表板”,則可能類似於:

SignInStatus。成功案例:

case SignInStatus.Success:
    return RedirectToAction("Dashboard");

如果需要返回許多動作的功能,則可以返回動作名稱而不是url,只需執行RedirectToAction(returnAction) 顯然,如果還需要指定一個控制器,則也需要發布一個returnController

儀表板動作:

[Authorize]
public ActionResult Dashboard() {
    var userId = User.Identity.GetUserId();

    Session["HasAgreement"] = Agreement.HasAgreement(userId);

    return View();
}

您的問題不是更新會話變量時,而是布局頁面獲得會話的哪個版本。

會話不是在視圖之間傳遞數據的最佳選擇。 嘗試使用ViewBag

(盡管您應該始終盡可能嘗試使用ViewModel!)(並且可以將會話AS WELL用於下一頁加載)

我不確定您的Agreement對象來自何處,但是您可以訪問“ View中的“ User屬性,因此可以執行以下操作:

_Layout.cshtml

@if (Agreement.HasAgreement(User.Identity.GetUserId()))
{
   <li>@Html.ActionLink("Agreement", "Agreement", "Home")</li>
}

這也假設HasAgreement返回一個bool ,如果沒有,則確實應該。

這可能並非在所有情況下都可行,但是我發現在Login上設置會話變量的最簡單方法是將邏輯移至RedirectToLocal()返回的控制器動作。 在我的情況下,我為應用程序創建了一個新的登錄后入口點,稱為“ Main”,它具有自己的視圖/控制器。 登錄時,始終總是首先將用戶重定向到此處,因此可以確保設置我的會話數據。

首先,我在AccountController更改了RedirectToLocal()

private ActionResult RedirectToLocal(string returnUrl)
{
    if (Url.IsLocalUrl(returnUrl))
    {
        return Redirect(returnUrl);
    }
    //This is the template default   
    //return RedirectToAction("Index", "Home");         

    //This is my new entry point. 
    return    RedirectToAction("Index", "Main");     }

在我的MainController內部,我可以訪問User對象並設置數據:

// GET: Main
public ActionResult Index()
{
    ApplicationUser sessionuser = db.Users.Find(User.Identity.GetUserId());
    Session.Add("UserName", sessionuser.UserName);
    return View();
}

而且,在注銷時,我會擦除會話數據:

public ActionResult LogOff()
{
    Session.RemoveAll(); //Clear all session variables
    //...              
}

作為選擇:

添加新的局部視圖,將其稱為Agreement

@model bool
@if (Model)
{
   <li>@Html.ActionLink("Agreement", "Agreement", "Home")</li>
}

向您的Account控制者添加新操作,將其稱為Agreement

public PartialViewResult Agreement(){
    var userId = User.Identity.GetUserId();
    bool hasAgreement = Agreement.HasAgreement(userId); // This will be your model
    return PartialView("Agreement", hasAgreement);
}

並在您的布局中執行以下操作:

@Html.RenderAction("Agreement", "Account")

我有完全相同的問題,並且似乎正在使用與您(OP)相同的身份版本。 我嘗試了您做過的事情(在發現之前),並按照Sippy的建議,將其放在AccountController RedirectToLocal ActionResult中(無效):

同時,我將其放在Global.Asax.cs中:

public void Profile_OnMigrateAnonymous(object sender, ProfileMigrateEventArgs args)
{
    ...//other code (or not) on migrate anonymous user to authenticated
    Session["HasAgreement"] = Agreement.HasAgreement(userId);
}

我還在Global.asax.cs中創建了此空缺(捕獲了過期的會話,但仍已登錄):

void Session_Start(object sender, EventArgs e)
{
     Session["HasAgreement"] = Agreement.HasAgreement(userId);
}

它可以正常工作,如果會話過期,它也會更新。 但是,這樣做的缺點之一是,當用戶注銷(通過身份)時,會話變量將不會更新。 但是,我也嘗試將我的客戶端擴展放在AuthenticationManager.SignOut();之后的LogOff ActionResult AuthenticationManager.SignOut(); 而且不起作用。

我也需要在注銷后更新會話變量,所以這不是我的最終解決方案,但這對您來說足夠好了嗎?

如果找到更好的方法,我會回來更新此方法,但是現在,它已經足夠好了,並列在我的TODO列表中。

更新:

為了捕獲用戶的注銷事件,我使用了Sippy的想法,並將AccountController LogOff ActionResult更改為此:

        //
        // POST: /Account/LogOff
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult LogOff()
        {
            AuthenticationManager.SignOut();
            return RedirectToAction("SetSessionVariables", "Account");
        }

        [AllowAnonymous]
        public ActionResult SetSessionVariables()
        {
            Session["HasAgreement"] = Agreement.HasAgreement(userId);
            return RedirectToAction("Index", "Home");
        }

我認為這涵蓋了所有登錄/注銷/會話方案。 我可能會看到是否可以將對LogOff所做的工作整合到登錄成功重定向中(更多的是查看用戶何時實際上已“經過身份驗證”)。

您可以使用用戶聲明來擴展身份數據。

此處的示例實現: 如何擴展User.Identity的可用屬性

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM