问题描述
我需要做一些相当简单的事情:在我的 ASP.NET MVC 应用程序中,我想设置一个自定义的 IIdentity/IPrincipal.哪个更容易/更合适.我想扩展默认值,以便我可以调用诸如 User.Identity.Id
和 User.Identity.Role
之类的东西.没什么特别的,只是一些额外的属性.
I need to do something fairly simple: in my ASP.NET MVC application, I want to set a custom IIdentity / IPrincipal. Whichever is easier / more suitable. I want to extend the default so that I can call something like User.Identity.Id
and User.Identity.Role
. Nothing fancy, just some extra properties.
我阅读了大量文章和问题,但我觉得我让它变得比实际更难.我以为这会很容易.如果用户登录,我想设置一个自定义的 IIdentity.所以我想,我将在我的 global.asax 中实现 Application_PostAuthenticateRequest
.但是,这是在每个请求上调用的,我不想在每个请求上都调用数据库,这将请求数据库中的所有数据并放入自定义 IPrincipal 对象.这似乎也是非常不必要的、缓慢的并且在错误的地方(在那里进行数据库调用),但我可能是错的.或者这些数据来自哪里?
I've read tons of articles and questions but I feel like I'm making it harder than it actually is. I thought it would be easy. If a user logs on, I want to set a custom IIdentity. So I thought, I will implement Application_PostAuthenticateRequest
in my global.asax. However, that is called on every request, and I don't want to do a call to the database on every request which would request all the data from the database and put in a custom IPrincipal object. That also seems very unnecessary, slow, and in the wrong place (doing database calls there) but I could be wrong. Or where else would that data come from?
所以我想,每当用户登录时,我都可以在我的会话中添加一些必要的变量,我将这些变量添加到 Application_PostAuthenticateRequest
事件处理程序中的自定义 IIdentity.但是,我的 Context.Session
在那里是 null
,所以这也不是要走的路.
So I thought, whenever a user logs in, I can add some necessary variables in my session, which I add to the custom IIdentity in the Application_PostAuthenticateRequest
event handler. However, my Context.Session
is null
there, so that is also not the way to go.
我已经为此工作了一天,但我觉得我错过了一些东西.这应该不难做到吧?我也对随之而来的所有(半)相关内容感到有些困惑.MembershipProvider
、MembershipUser
、RoleProvider
、ProfileProvider
、IPrincipal
、IIdentity
, FormsAuthentication
.... 只有我一个人觉得这一切很混乱吗?
I've been working on this for a day now and I feel I'm missing something. This shouldn't be too hard to do, right? I'm also a bit confused by all the (semi)related stuff that comes with this. MembershipProvider
, MembershipUser
, RoleProvider
, ProfileProvider
, IPrincipal
, IIdentity
, FormsAuthentication
.... Am I the only one who finds all this very confusing?
如果有人能告诉我一个简单、优雅和有效的解决方案,在 IIdentity 上存储一些额外的数据,而无需所有额外的模糊......那就太好了!我知道在 SO 上也有类似的问题,但如果我需要的答案就在那里,我一定是忽略了.
If someone could tell me a simple, elegant, and efficient solution to store some extra data on a IIdentity without all the extra fuzz.. that would be great! I know there are similar questions on SO but if the answer I need is in there, I must've overlooked.
推荐答案
这是我的做法.
我决定使用 IPrincipal 而不是 IIdentity,因为这意味着我不必同时实现 IIdentity 和 IPrincipal.
I decided to use IPrincipal instead of IIdentity because it means I don't have to implement both IIdentity and IPrincipal.
创建界面
Create the interface
interface ICustomPrincipal : IPrincipal
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
}
自定义主体
CustomPrincipal
public class CustomPrincipal : ICustomPrincipal
{
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return false; }
public CustomPrincipal(string email)
{
this.Identity = new GenericIdentity(email);
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
CustomPrincipalSerializeModel - 用于将自定义信息序列化到 FormsAuthenticationTicket 对象中的用户数据字段中.
CustomPrincipalSerializeModel - for serializing custom information into userdata field in FormsAuthenticationTicket object.
public class CustomPrincipalSerializeModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
登录方法 - 使用自定义信息设置 cookie
LogIn method - setting up a cookie with custom information
if (Membership.ValidateUser(viewModel.Email, viewModel.Password))
{
var user = userRepository.Users.Where(u => u.Email == viewModel.Email).First();
CustomPrincipalSerializeModel serializeModel = new CustomPrincipalSerializeModel();
serializeModel.Id = user.Id;
serializeModel.FirstName = user.FirstName;
serializeModel.LastName = user.LastName;
JavaScriptSerializer serializer = new JavaScriptSerializer();
string userData = serializer.Serialize(serializeModel);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
viewModel.Email,
DateTime.Now,
DateTime.Now.AddMinutes(15),
false,
userData);
string encTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
Response.Cookies.Add(faCookie);
return RedirectToAction("Index", "Home");
}
Global.asax.cs - 读取 cookie 并替换 HttpContext.User 对象,这是通过覆盖 PostAuthenticateRequest 来完成的
Global.asax.cs - Reading cookie and replacing HttpContext.User object, this is done by overriding PostAuthenticateRequest
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JavaScriptSerializer serializer = new JavaScriptSerializer();
CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
CustomPrincipal newUser = new CustomPrincipal(authTicket.Name);
newUser.Id = serializeModel.Id;
newUser.FirstName = serializeModel.FirstName;
newUser.LastName = serializeModel.LastName;
HttpContext.Current.User = newUser;
}
}
在 Razor 视图中访问
Access in Razor views
@((User as CustomPrincipal).Id)
@((User as CustomPrincipal).FirstName)
@((User as CustomPrincipal).LastName)
并在代码中:
(User as CustomPrincipal).Id
(User as CustomPrincipal).FirstName
(User as CustomPrincipal).LastName
我认为代码是不言自明的.如果不是,请告诉我.
I think the code is self-explanatory. If it isn't, let me know.
此外,为了使访问更加容易,您可以创建一个基本控制器并覆盖返回的用户对象 (HttpContext.User):
Additionally to make the access even easier you can create a base controller and override the returned User object (HttpContext.User):
public class BaseController : Controller
{
protected virtual new CustomPrincipal User
{
get { return HttpContext.User as CustomPrincipal; }
}
}
然后,对于每个控制器:
and then, for each controller:
public class AccountController : BaseController
{
// ...
}
这将允许您在代码中访问自定义字段,如下所示:
which will allow you to access custom fields in code like this:
User.Id
User.FirstName
User.LastName
但这在视图中不起作用.为此,您需要创建一个自定义的 WebViewPage 实现:
But this will not work inside views. For that you would need to create a custom WebViewPage implementation:
public abstract class BaseViewPage : WebViewPage
{
public virtual new CustomPrincipal User
{
get { return base.User as CustomPrincipal; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new CustomPrincipal User
{
get { return base.User as CustomPrincipal; }
}
}
使其成为 Views/web.config 中的默认页面类型:
Make it a default page type in Views/web.config:
<pages pageBaseType="Your.Namespace.BaseViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
在视图中,您可以像这样访问它:
and in views, you can access it like this:
@User.FirstName
@User.LastName
这篇关于ASP.NET MVC - 设置自定义 IIdentity 或 IPrincipal的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!