asp.net - 快速修改问答信息 ASP.NET MVC - - 设置自定义IIdentity或者IPrincipal

  显示原文与译文双语对照的内容

我需要做一些非常简单的事情: 在我的ASP.NET MVC应用程序中,我想设置一个定制的iidentity/rr 。 哪个更容易/更合适。我想扩展默认值,这样我可以调用像 User.Identity.IdUser.Identity.Role 这样的东西。 没什么新奇的,只是一些额外的属性。

我已经阅读了大量的文章和问题,但我觉得我比实际更难。 我觉得这很容易。 如果一个用户登录,我想设置一个自定义iidentity。 所以我想,我将实现 Application_PostAuthenticateRequest 但是,在我的global.asax. 中,每个请求都调用了这个调用,我不想对每个请求调用数据库,请求来自数据库的所有数据并放入自定义IPrincipal对象。 这似乎是不必要的,慢,而且在错误的地方,( 正在进行数据库调用) 但我可以错。 或者其他数据来自哪里?

所以我想,每当用户登录时,我都可以在我的会话中添加一些必要的变量,我将这些变量添加到 Application_PostAuthenticateRequest 事件处理程序。但是,我的Context.Sessionnull,所以这也不是。

我在这一天工作了一天,我觉得我缺少了一些东西。 这应该不难做到,对吧? 我也有点迷惑,所有的( 半) 相关的东西,伴随着。 MembershipProviderMembershipUserRoleProviderProfileProviderIPrincipalIIdentityFormsAuthentication 。。 我是唯一一个发现这一切非常混乱的人?

如果有人能告诉我一个简单的,优雅的,高效的解决方案来存储一些额外的数据在一个 IIdentity,没有多余的模糊。 那太好了我知道有类似的问题,但是如果我需要的答案是在那里,我必须忽略。 谢谢。

时间:

我就是这么做的。

我决定使用IPrincipal代替 IIdentity,因为这意味着我不必同时实现IIdentity和 IPrincipal 。

  1. 创建接口

    
    interface ICustomPrincipal : IPrincipal
    {
     int Id { get; set; }
     string FirstName { get; set; }
     string LastName { get; set; }
    }
    
    
  2. 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; }
    }
    
    
  3. CustomPrincipalSerializeModel - 用于将自定义信息序列化到FormsAuthenticationTicket对象中的userdata字段。

    
    public class CustomPrincipalSerializeModel
    {
     public int Id { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }
    }
    
    
  4. 登录方法- 使用自定义信息设置 cookie

    
    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");
    }
    
    
  5. Global.asax.cs - 读取cookie并替换 HttpContext.User 对象,这是通过重写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;
     }
    }
    
    
  6. Razor 视图中的访问

    
    @((User as CustomPrincipal).Id)
    @((User as CustomPrincipal).FirstName)
    @((User as CustomPrincipal).LastName)
    
    

在代码中:


 (User as CustomPrincipal).Id
 (User as CustomPrincipal).FirstName
 (User as CustomPrincipal).LastName

我认为代码是 self-explanatory 。 如果不是,让我知道。

另外,为了使访问更容易,你可以创建一个基本控制器并覆盖返回的用户对象( httpcontext.user ):


public class BaseController : Controller
{
 protected virtual new CustomPrincipal User
 {
 get { return HttpContext.User as CustomPrincipal; }
 }
}

然后,对于每个控制器:


public class AccountController : BaseController
{
//...
}

这将允许你在代码中访问自定义字段:


User.Id
User.FirstName
User.LastName

但这在视图中不起作用。 因为你需要创建一个定制的WebViewPage实现:


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; }
 }
}

将它的作为视图/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>

在视图中,你可以像这样访问它:


@User.FirstName
@User.LastName

HTH

我不能直接为 ASP.NET MVC说话,但是对于 ASP.NET Web表单,窍门是创建一个 FormsAuthenticationTicket 并在用户经过身份验证后将它的加密到cookie中。 这样,你只需要一次调用数据库一次( 或者广告或者任何你用来执行认证的东西),随后的每个请求都将基于存储在cookie中的票证进行身份验证。

关于这个的一篇好文章: http://www.ondotnet.com/pub dotnet/1 2004/02/02 1/effectiveformsauth. html ( 断开的链接)

编辑:

由于上面的链接已损坏,我建议在下面的答案中解决lukep问题: http://stackoverflow.com 10524305 web - 我也建议将接受的答案更改为那个。

下面是一个完成任务的例子。 通过查看某些数据存储( 假设你的用户数据库) 设置 bool isValid 。 UserID只是我维护的一个标识。 你可以向用户数据添加aditional信息如电子邮件地址。


protected void btnLogin_Click(object sender, EventArgs e)
{ 
//Hard Coded for the moment
 bool isValid=true;
 if (isValid) 
 {
 string userData = String.Empty;
 userData = userData +"UserID=" + userID;
 FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddMinutes(30), true, userData);
 string encTicket = FormsAuthentication.Encrypt(ticket);
 HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
 Response.Cookies.Add(faCookie);
//And send the user where they were heading
 string redirectUrl = FormsAuthentication.GetRedirectUrl(username, false);
 Response.Redirect(redirectUrl);
 }
}

在 golbal asax中添加以下代码检索你的信息


protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
 HttpCookie authCookie = Request.Cookies[
 FormsAuthentication.FormsCookieName];
 if(authCookie!= null)
 {
//Extract the forms authentication cookie
 FormsAuthenticationTicket authTicket = 
 FormsAuthentication.Decrypt(authCookie.Value);
//Create an Identity object
//CustomIdentity implements System.Web.Security.IIdentity
 CustomIdentity id = GetUserIdentity(authTicket.Name);
//CustomPrincipal implements System.Web.Security.IPrincipal
 CustomPrincipal newUser = new CustomPrincipal();
 Context.User = newUser;
 }
}

在以后使用这些信息时,你可以访问自定义主体,如下所示。


(CustomPrincipal)this.User
or 
(CustomPrincipal)this.Context.User

这将允许你访问自定义用户信息。

MVC为你提供了从控制器类挂起的OnAuthorize方法。 或者,你可以使用自定义操作筛选器执行授权。 MVC使得它很容易做到。 我在这里发表了一篇关于这个的博客文章。 http://www.bradygaster.com/post/custom-authentication-with-mvc-3.0

下面是一个解决方案,如果你需要将一些方法连接到 @User 以便在你的视图中使用。 没有任何解决方案可以用于任何严重的成员自定义,但是如果只需要原始问题的原始问题,那么可能就足够了。 下面用于检查从authorizefilter返回的变量,用于验证是否有wehere链接被显示或者 not(not for any kind of authorization logic or access granting) 。


using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Web;
 using System.Security.Principal;

 namespace SomeSite.Web.Helpers
 {
 public static class UserHelpers
 {
 public static bool IsEditor(this IPrincipal user)
 {
 return null;//Do some stuff
 }
 }
 }

然后在区域 Web.Config 中添加一个引用,并在视图中调用它。

 
@User.IsEditor()

 

基于 lukep的应答,并添加一些方法来安装 timeoutrequireSSLWeb.config

引用链接

修改的LukeP

基于 Web.config 1,timeoutFormsAuthentication.Timeout 将获取超时值,它在 Web.Config 中定义。 我将以下内容包装为一个函数,返回一个 ticket


int version = 1;
DateTime now = DateTime.Now;

//respect to the `timeout` in Web.config.
TimeSpan timeout = FormsAuthentication.Timeout;
DateTime expire = now.Add(timeout);
bool isPersist = false;

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
 version, 
 name,
 now,
 expire,
 isPersist,
 userData);

2,根据 requireSSL 配置,将cookie配置为安全或者不安全。


HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
//respect to `RequreSSL` in `Web.Config`
bool bSSL = FormsAuthentication.RequireSSL;
faCookie.Secure = bSSL;

作为web表单用户除了LukeP代码( 不是 MVC ) 如果你想简化背后的代码中访问你的页面,将下面的代码添加到一个基地页面和推导出基地在所有页面:


Public Overridable Shadows ReadOnly Property User() As CustomPrincipal
 Get
 Return DirectCast(MyBase.User, CustomPrincipal)
 End Get
End Property

所以在你的代码中,你可以简单地访问:


User.FirstName or User.LastName

我丢失在web表单的情况下,如何在代码中获得相同的行为不是绑定到页面,例如在 httpmodules我应该总是在每个类添加一个演员或有聪明的方法来获得呢?

感谢你的回答和感谢 LukeP,因为我使用了你的示例作为我自定义用户的基础( 现在有 User.RolesUser.TasksUser.HasPath(int)User.Settings.Timeout 和许多其他好东西)

你可以打开 ASP.NET mvc 4 web应用程序模板项目( 至少在vs2013中),并查看它是如何实现的。

我会试着用它。

...