Asp.net MVC Позволяет пользователю переключаться между ролями
Я разрабатываю сложный веб-сайт с пользователями, имеющими несколько ролей. Пользователи также связаны с другими элементами в БД, которые вместе с их ролями определяют, что они могут видеть и делать на веб-сайте.
Теперь некоторые пользователи имеют более одной роли, но веб-сайт может обрабатывать только одну роль за раз из-за сложной структуры.
Идея состоит в том, что пользователь входит в систему и имеет выпадающий список в углу веб-сайта, где он может выбрать одну из своих ролей. если у него только одна роль, выпадающего списка нет.
Теперь я сохраняю последнее выбранное значение роли в БД вместе с другими его настройками пользователя. Когда он возвращается, таким образом роль все еще помнят.
Значение раскрывающегося списка должно быть доступно по всему сайту. Я хочу сделать 2 вещи:
- Сохранить текущую роль в
Session
, - Переопределить
IsInRole
метод или написатьIsCurrentlyInRole
метод, чтобы проверить весь доступ к выбранной в настоящее время роли, а не все роли, как это делает оригиналIsInRole
метод
Для хранения в сессионной части я подумал, что было бы хорошо сделать это в Global.asax
protected void Application_AuthenticateRequest(Object sender, EventArgs e) {
if (User != null && User.Identity.IsAuthenticated) {
//check for roles session.
if (Session["CurrentRole"] == null) {
NASDataContext _db = new NASDataContext();
var userparams = _db.aspnet_Users.First(q => q.LoweredUserName == User.Identity.Name).UserParam;
if (userparams.US_HuidigeRol.HasValue) {
var role = userparams.aspnet_Role;
if (User.IsInRole(role.LoweredRoleName)) {
//safe
Session["CurrentRole"] = role.LoweredRoleName;
} else {
userparams.US_HuidigeRol = null;
_db.SubmitChanges();
}
} else {
//no value
//check amount of roles
string[] roles = Roles.GetRolesForUser(userparams.aspnet_User.UserName);
if (roles.Length > 0) {
var role = _db.aspnet_Roles.First(q => q.LoweredRoleName == roles[0].ToLower());
userparams.US_HuidigeRol = role.RoleId;
Session["CurrentRole"] = role.LoweredRoleName;
}
}
}
}
}
но, видимо, это дает ошибки во время выполнения. Session state is not available in this context.
- Как мне это исправить, и действительно ли это лучшее место для размещения этого кода?
- Как продлить пользователя (
IPrincipal
?) сIsCurrentlyInRole
без потери всей другой функциональности - Может быть, я делаю все это неправильно, и есть лучший способ сделать это?
Любая помощь с благодарностью.
2 ответа
Да, вы не можете получить доступ к сеансу в Application_AuthenticateRequest.
Я создал свой собственный CustomPrincipal. Я покажу вам пример того, что я недавно сделал:
public class CustomPrincipal: IPrincipal
{
public CustomPrincipal(IIdentity identity, string[] roles, string ActiveRole)
{
this.Identity = identity;
this.Roles = roles;
this.Code = code;
}
public IIdentity Identity
{
get;
private set;
}
public string ActiveRole
{
get;
private set;
}
public string[] Roles
{
get;
private set;
}
public string ExtendedName { get; set; }
// you can add your IsCurrentlyInRole
public bool IsInRole(string role)
{
return (Array.BinarySearch(this.Roles, role) >= 0 ? true : false);
}
}
Мой Application_AuthenticateRequest считывает куки, если есть билет аутентификации (пользователь вошел в систему):
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[My.Application.FORMS_COOKIE_NAME];
if ((authCookie != null) && (authCookie.Value != null))
{
Context.User = Cookie.GetPrincipal(authCookie);
}
}
public class Cookie
{
public static IPrincipal GetPrincipal(HttpCookie authCookie)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket != null)
{
string ActiveRole = "";
string[] Roles = { "" };
if ((authTicket.UserData != null) && (!String.IsNullOrEmpty(authTicket.UserData)))
{
// you have to parse the string and get the ActiveRole and Roles.
ActiveRole = authTicket.UserData.ToString();
Roles = authTicket.UserData.ToString();
}
var identity = new GenericIdentity(authTicket.Name, "FormAuthentication");
var principal = new CustomPrincipal(identity, Roles, ActiveRole );
principal.ExtendedName = ExtendedName;
return (principal);
}
return (null);
}
}
Я расширил файл cookie, добавив в него UserData аутентификационного билета. Я поместил дополнительную информацию здесь:
Это функция, которая создает куки после входа в систему:
public static bool Create(string Username, bool Persistent, HttpContext currentContext, string ActiveRole , string[] Groups)
{
string userData = "";
// You can store your infos
userData = ActiveRole + "#" string.Join("|", Groups);
FormsAuthenticationTicket authTicket =
new FormsAuthenticationTicket(
1, // version
Username,
DateTime.Now, // creation
DateTime.Now.AddMinutes(My.Application.COOKIE_PERSISTENCE), // Expiration
Persistent, // Persistent
userData); // Additional informations
string encryptedTicket = System.Web.Security.FormsAuthentication.Encrypt(authTicket);
HttpCookie authCookie = new HttpCookie(My.Application.FORMS_COOKIE_NAME, encryptedTicket);
if (Persistent)
{
authCookie.Expires = authTicket.Expiration;
authCookie.Path = FormsAuthentication.FormsCookiePath;
}
currentContext.Response.Cookies.Add(authCookie);
return (true);
}
Теперь вы можете получить доступ к своей информации в любом месте вашего приложения:
CustomPrincipal currentPrincipal = (CustomPrincipal)HttpContext.User;
так что вы можете получить доступ к вашим пользовательским основным членам: currentPrincipal.ActiveRole
Когда пользователь меняет свою роль (активную роль), вы можете переписать cookie.
Я забыл сказать, что я храню в authTicket.UserData сериализованный JSON класс, поэтому его легко десериализовать и проанализировать.
Вы можете найти больше информации здесь
Если вы действительно хотите, чтобы у пользователя была только одна активная роль за один раз (что подразумевается при желании переопределить IsInRole), возможно, было бы проще всего хранить все "потенциальные" роли пользователя в отдельном месте, но только фактически разрешать их. быть в одной роли аутентификации ASP.NET одновременно. Когда они выбирают новую роль, используйте встроенные методы, чтобы удалить их из текущей роли и добавить их в новую.