基于用户角色的ASP.NET MVC 5 Identity 2登录重定向

14

我试图根据用户的角色将其重定向到不同的页面。

这是ASP.NET MVC 5自带的登录函数的默认实现:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindAsync(model.UserName, model.Password);
        if (user != null)
        {
            await SignInAsync(user, model.RememberMe);
            return RedirectToLocal(returnUrl);
        }
        else
        {
            ModelState.AddModelError("", "Invalid username or password.");
        }
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

private ActionResult RedirectToLocal(string returnUrl)
{
    if (Url.IsLocalUrl(returnUrl))
    {
        return Redirect(returnUrl);
    }
    else
    {
        return RedirectToAction("Index", "Employer");
    }
}

我希望能够根据用户的角色进行重定向,我尝试了以下方式:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindAsync(model.UserName, model.Password);
        if (user != null)
        {
            await SignInAsync(user, model.RememberMe);

            //role Employer go to Employer page
            if (UserManager.IsInRole(user.Id, "Employer"))
            {
                return RedirectToAction("Index", "Employer");
            }
            //role Admin go to Admin page
            else if (UserManager.IsInRole(user.Id, "Admin"))
            {
                return RedirectToAction("Index", "Admin");
            }
            else
            {
                //no role
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("", "Invalid username or password.");
        }
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}
但是有一个问题,虽然该网站将我重定向到了正确的页面,但如果在未使用管理员帐户登录时通过键入URL foo.com/admin进行导航,则该网站会将我带到登录页面,URL为foo.com/Account/Login?ReturnUrl=%2Fadmin,这是预期的行为。
如果此时我使用雇主帐户登录,它将重定向我到雇主页面并将我作为雇主登录,这并不是错误的,但不应该发生这种情况,网站应该提醒我应该使用管理员帐户登录,因为返回URL是"admin"。我希望我的意思表达清楚。
2个回答

21

为什么你不在自定义重定向之前检查是否存在returnUrl?

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
         if (ModelState.IsValid)
         {
              var user = await UserManager.FindAsync(model.UserName, model.Password);
              if (user != null)
              {
                    await SignInAsync(user, model.RememberMe);
                    if (String.IsNullOrEmpty(returnUrl))
                    {
                         if (UserManager.IsInRole(user.Id, "Employer"))
                         {
                             return RedirectToAction("Index", "Employer");
                         }
                         //role Admin go to Admin page
                         if (UserManager.IsInRole(user.Id, "Admin"))
                         {
                             return RedirectToAction("Index", "Admin");
                         }
                     }
                     else
                     {
                         return RedirectToLocal(returnUrl);
                     }
              }
              else
              {
                     ModelState.AddModelError("", "Invalid username or password.");
              }
       }

       // If we got this far, something failed, redisplay form
       return View(model);
   }

如果您导航到foo.com/admin,它会抛出401并将您重定向到登录页。然后,如果您以雇主身份登录,它会再次抛出401并将您重定向回登录页。

从评论中得知:“我可以只使用Redirect(returnUrl)并删除RedirectToLocal操作方法吗?”

RedirectToLocal(returnUrl) 方法检查 Url.IsLocalUrl(returnUrl)是否成立,因此需要防止开放式重定向攻击。


你好,能否请您解释一下为什么需要 else 语句返回 RedirectToLocal(returnUrl)? - Mindless
你需要它是因为要检查 if(Url.IsLocalUrl(returnUrl))。 - tmg
@tmg,我按照你的方法操作,非常准确,对我很有帮助。但是,当我们在“EmployerController”类的索引操作上方放置“[Authorize(Roles = "Employer")]”,并且在“AdminController”类的索引操作上方放置“[Authorize(Roles = "Admin")]”时,这种方法不起作用。但是,如果没有这些授权,我该如何限制未经授权访问这些页面? - Kelum
@kelum 我也使用了Authorize属性来装饰这些控制器(以限制访问),并且它们可以正常工作。可能是其他地方出了问题,或者我没有正确理解你的意思。 - tmg
1
你应该像这样检查返回的URL:如果(!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); }出于安全考虑。 - user10997800
显示剩余3条评论

1
尽管 [this] 的文章是在2008年写的,但它帮助我解决了这个问题。它提供了所有需要的代码示例,以便在不混乱您的登录方法的情况下重定向用户。如果您添加了新角色并希望重定向具有该角色的用户,只需在web.config中添加一行即可。
我确实遇到了一个小问题。在他的文章中,他有以下代码,您可以将其放在您的AccountController(或任何您想要执行重定向的位置):
/// <summary>
/// Redirect the user to a specific URL, as specified in the web.config, depending on their role.
/// If a user belongs to multiple roles, the first matching role in the web.config is used.
/// Prioritize the role list by listing higher-level roles at the top.
/// </summary>
/// <param name="username">Username to check the roles for</param>
private void RedirectLogin(string username)
{
    LoginRedirectByRoleSection roleRedirectSection = (LoginRedirectByRoleSection)ConfigurationManager.GetSection("loginRedirectByRole");
    foreach (RoleRedirect roleRedirect in roleRedirectSection.RoleRedirects)
    {
        if (Roles.IsUserInRole(username, roleRedirect.Role))
        {
            Response.Redirect(roleRedirect.Url);
        }
    }
}

我的应用程序无法找到角色提供程序,当我在web.config中添加一个时,我很难让它找到我的角色。我决定放弃角色提供程序,改用UserManager来获取用户和角色:
    /// <summary>
    /// Redirect the user to a specific URL, as specified in the web.config, depending on their role.
    /// If a user belongs to multiple roles, the first matching role in the web.config is used.
    /// Prioritize the role list by listing higher-level roles at the top.
    /// </summary>
    /// <param name="username">Username to check the roles for</param>
    private void RedirectLogin(string username)
    {
        LoginRedirectByRoleSection roleRedirectSection = (LoginRedirectByRoleSection)ConfigurationManager.GetSection("loginRedirectByRole");
        var user = UserManager.FindByName(username);
        var rolesForUser = UserManager.GetRoles(user.Id);
        foreach (RoleRedirect roleRedirect in roleRedirectSection.RoleRedirects)
        {
            if (rolesForUser.Contains(roleRedirect.Role))
            {
                Response.Redirect(roleRedirect.Url);
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接