9

我编写了一个自定义主体对象,其中包含一些附加字段(除了用户名之外的电子邮件和用户 ID)。

为了访问这些属性,我必须将 Context.User 对象转换为我的自定义主体。

@Html.GetGravitarImage((User as CustomPrincipal).Email)

这个自定义主体是通过我的 global.ascx 中的 Application_AuthenticateRequest 创建/反序列化的。您可以查看我在此处询问的这个问题以获取更多信息。

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) 的结果将返回 null,从而给我一个 null 引用当我上面的方法尝试获取电子邮件时出现异常。

什么是这个问题的干净解决方案?我想让访问我的自定义主体变得容易,并且必须执行以下操作似乎很麻烦:

@Html.GetGravitarIcon((User is CustomPrincipal) ? (User as CustomPrincipal).Email : "Default Email")

这是处理这种情况的唯一方法吗?

4

6 回答 6

3

我快速地把东西搅在一起。在 ASP.NET MVC 中轻松引入自定义 IPrincipal 的一种可能方法如下:

1) 创建您自己的 IPrincipal 接口的后代。

public interface IMyPrincipal : IPrincipal
{
    Guid UserId { get; }
    string EmailAddress { get; }
}

2) 假设您正在使用 ASP.NET Membership 提供程序来验证您的用户。让我们快速构建一个利用会员 API 的 IMyPrincipal 实现。

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) 在 Global.asax 的 Application_Start() 事件处理程序中注册控制器工厂。

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();
}
于 2011-08-13T15:25:55.893 回答
2

您可以创建一个基类并使用“new”关键字覆盖“User”属性,或者创建一个扩展方法,如下所示:

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...
    }
}
于 2011-08-12T18:19:15.087 回答
1

使用 ASP.NET MVC使您的IPrincipal实现在 Razor 页面中可访问的最佳方法是执行以下操作:

  1. 实现System.Security.Principal.IPrincipal接口。
  2. 实现System.Security.Principal.IIdentity接口。
  3. Global.asax为: 定义一个方法,void Application_AuthenticateRequest(Object, EventArgs)它会保留你的IPrincipal和的实现IIdentity
  4. 创建一个扩展方法IPrincipal以公开您的IIdentity.
  5. 最后,在您的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

于 2012-11-23T13:48:44.240 回答
0

您可以创建某种实用程序方法或将方法添加到您的一个服务中,以检查它是否是您的自定义主体。也许是这样的:

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;
        }
    }
}
于 2011-08-12T18:24:48.893 回答
0

您还可以按照与 John Kalberer 的回答相同的方式为电子邮件和用户 ID 制作扩展方法:

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"
    }
}
于 2011-08-12T18:26:39.493 回答
0

如果未授权,您可以将用户对象设置为具有默认值的自定义主体的特定实例:

if (authCookie == null)
{
    context.User = CustomPrincipal.Default; // Or CreateDefault()
    return;
}
于 2011-08-12T18:27:03.870 回答