Как я могу облегчить доступ к своему пользовательскому IPrincipal в ASP.NET MVC?
Я написал собственный основной объект, который содержит несколько дополнительных полей (адрес электронной почты и идентификатор пользователя в дополнение к имени пользователя).
Чтобы получить доступ к этим свойствам, я должен привести объект Context.User в качестве своего пользовательского участника.
@Html.GetGravitarImage((User as CustomPrincipal).Email)
Этот пользовательский принципал создается / десериализуется через Application_AuthenticateRequest в моем global.ascx. Вы можете увидеть этот вопрос, который я задал здесь для получения дополнительной информации.
private void Application_AuthenticateRequest(Object source, EventArgs e)
{
var application = (HttpApplication)source;
var context = application.Context;
// Get the authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = context.Request.Cookies[cookieName];
if (authCookie == null)
return;
var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
context.User = CustomPrincipal.CreatePrincipalFromCookieData(authTicket.UserData);
}
Однако, если пользователь не аутентифицирован, то приведение к CustomPrincipal завершится неудачно (потому что он не будет введен в методе, описанном выше), а результат (User as CustomPrincipal) вернет ноль, таким образом, давая мне нулевую ссылку исключение, когда мой метод выше пытается получить электронную почту.
Каково было бы чистое решение этой проблемы? Я хочу сделать доступ к своему пользовательскому принципалу легким, а выполнение следующих действий кажется громоздким:
@Html.GetGravitarIcon((User is CustomPrincipal) ? (User as CustomPrincipal).Email : "Default Email")
Это единственный способ справиться с этой ситуацией?
6 ответов
Я быстро что-то взбил. Один из возможных способов легко ввести пользовательский IPrincipal в ASP.NET MVC заключается в следующем:
1) Создайте своего собственного потомка интерфейса IPrincipal.
public interface IMyPrincipal : IPrincipal
{
Guid UserId { get; }
string EmailAddress { get; }
}
2) Давайте предположим, что вы используете провайдера ASP.NET для проверки подлинности ваших пользователей. Давайте быстро создадим реализацию IMyPrincipal, которая использует API членства.
public class MyPrincipal : IMyPrincipal
{
private MembershipUser _user;
public MyPrincipal()
{
this._user = Membership.GetUser();
var userName = this._user != null ? this._user.UserName : String.Empty;
this.Identity = new GenericIdentity(userName);
}
public Guid UserId
{
get
{
return this._user != null ? (Guid) this._user.ProviderUserKey :
default(Guid);
}
}
public string EmailAddress
{
get
{
return this._user != null ? this._user.Email : null;
}
}
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return false; }
}
3) Создайте свой собственный тип базового класса для ваших контроллеров. Скройте унаследованный член User и представьте своего собственного IPrincipal потомка.
public class BaseController : Controller
{
protected virtual new MyPrincipal User
{
get { return HttpContext.User as MyPrincipal; }
}
}
4) Пусть все ваши контроллеры происходят от этого нового типа BaseController.
public class HomeController : BaseController
{
//...
}
5) Создайте свою собственную фабрику контроллеров, чтобы убедиться, что ваш принципал введен в HttpContext / Thread.
public class MyControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance
(RequestContext requestContext, Type controllerType)
{
try
{
var controller = base.GetControllerInstance(requestContext, controllerType);
requestContext.HttpContext.User = Thread.CurrentPrincipal = new
MyPrincipal();
return controller;
}
catch (Exception)
{
return base.GetControllerInstance(requestContext, controllerType);
}
}
}
6) Зарегистрируйте фабрику контроллера в обработчике событий Application_Start() Global.asax.
var controllerFactory = new MyControllerFactory();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
Вуаля, теперь вы можете использовать нового пользователя (IMyPrincipal) в любом месте ваших контроллеров.
Например:
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
ViewBag.UserId = User.UserId;
ViewBag.UserName = User.EmailAddress;
return View();
}
Вы можете либо создать базовый класс и переопределить свойство "Пользователь", используя ключевое слово "new", либо создать метод расширения, подобный этому:
public static class ControllerExtensions
{
public static CustomPrincipal CustomPrincipal(this Controller controller)
{
if(controller.User is CustomPrincipal)
{
return controller.User as CustomPrincipal;
}
return null; // maybe return an empty object instead to get around null reference...
}
}
Лучший способ сделать ваш IPrincipal
Реализация, доступная на ваших страницах Razor с использованием ASP.NET MVC, делает следующее:
- Реализовать
System.Security.Principal.IPrincipal
интерфейс. - Реализовать
System.Security.Principal.IIdentity
интерфейс. - В
Global.asax
определить метод для:void Application_AuthenticateRequest(Object, EventArgs)
что сохраняется ваши обе реализацииIPrincipal
а такжеIIdentity
, - Создать метод расширения для
IPrincipal
разоблачить вашу реализациюIIdentity
, - Наконец, добавьте пространство имен для предыдущего метода расширения в файл web.config в
<system.web.webPages.razor>
,
В конце вы сможете получить доступ к своей пользовательской реализации IIdentity
вместо литья типа. Теперь вы можете получить доступ к своей пользовательской реализации следующим образом:
Hello @User.CustomIdentity().FirstName @User.CustomerIdentity().LastName!
Эти шаги представляют собой краткое и подробное описание хорошо подробной статьи, написанной здесь: http://rizzo7.blogspot.com/2012/04/mvc-30-razor-custom-principal-and.html
Вы также можете создать методы расширения для Email и UserID так же, как ответ Джона Калберера:
public static class CustomPrincipalExtensions
{
public static string Email(this CustomPrincipal cUser)
{
return cUser != null ? cUser.Email : "Default Email"
}
public static string UserID(this CustomPrincipal cUser)
{
return cUser != null ? cUser.UserID : "Default ID"
}
}
Если вы не авторизованы, вы можете установить для объекта пользователя определенный экземпляр пользовательского участника со значениями по умолчанию:
if (authCookie == null)
{
context.User = CustomPrincipal.Default; // Or CreateDefault()
return;
}
Вы можете создать какой-либо служебный метод или добавить метод к одному из ваших сервисов, который проверяет, является ли он вашим пользовательским принципалом. Может быть что-то вроде:
public class UserService : IUserService
{
public CustomPrincipal CurrentUser
{
get
{
CustomPrincipal user = HttpContext.Current.User as CustomPrincipal;
if (user == null)
return GuestUser; // Just some default user object
return user;
}
}
}