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);
}
Но есть проблема, хотя сайт перенаправляет меня на правильную страницу. Если я нахожусь, набрав url foo.com/admin, когда я не войду в систему с учетной записью администратора, сайт приведет меня к страницу входа с URL-адресом foo.com/Account/Login?ReturnUrl=%2Fadmin, что является ожидаемым поведением.
если я вхожу в систему с учетной записью работодателя на этом этапе, он перенаправит меня на страницу работодателя и запишет меня в качестве работодателя, что не так, но это не должно быть так, сайт должен упомянуть меня должен войти в систему с учетной записью администратора, потому что возвращаемый URL-адрес "admin". Надеюсь, у меня есть смысл.
Ответы
Ответ 1
почему вы не проверяете, есть ли 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 и снова перенаправит вас на логин.
Из комментариев: "Могу ли я просто перенаправить (returnUrl) и удалить метод действия RedirectToLocal?"
RedirectToLocal(returnUrl)
проверяет, есть ли Url.IsLocalUrl(returnUrl)
. Таким образом, это необходимо для предотвращения Open Redirect Attacks.
Ответ 2
Несмотря на [эту статью, написанную в 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);
}
}
}