web-dev-qa-db-ja.com

ユーザーロールに基づくASP.NET MVC 5 Identity 2ログインリダイレクト

ユーザーの役割に基づいてユーザーをページにリダイレクトしようとしていますが、

これは、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);
}

しかし、問題があります。サイトが正しいページにリダイレクトしていますが、管理者アカウントでログインしていないときにfoo.com/adminのURLを入力してナビゲートすると、サイトは次のようなログインページに移動します。 URL foo.com/Account/Login?ReturnUrl=%2Fadmin、これは予想される動作です。

この時点で雇用主のアカウントでログインすると、雇用主のページにリダイレクトされ、雇用主としてログインしますが、これは間違いではありませんが、そうではないはずです。戻りURLが「admin」であるため、代わりに管理者アカウント。私は理にかなっていると思います。

14
Mindless

カスタムリダイレクトの前に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)かどうかをチェックします。したがって、オープンリダイレクト攻撃を防ぐために必要です。

20
tmg

[ this ]の記事は2008年に書かれていますが、この問題の解決に役立ちました。 Loginメソッドを混乱させることなく、ログイン時にユーザーをリダイレクトするために必要なすべてのコードサンプルを提供します。新しいロールを追加して、そのロールを持つユーザーをリダイレクトする場合は、web.configに行を追加するだけです。

私は1つの問題に出くわしました。彼の記事には、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);
            }
        }
    }
1
Daryl