ASP.NET MVC 中的自定义主体

发布于 2024-09-24 16:43:19 字数 1043 浏览 8 评论 0原文

我希望能够访问经过身份验证的用户的自定义属性(例如 UserId 和 FirstName),而无需每次都查询数据库。我发现此网站通过在 Stack Overflow 上发帖,我喜欢这种方法 - 但我使用 IoC/存储库,并决定不尝试让 global.asax 与数据库通信,因为担心它与存储库模式不兼容。

相反,我创建了一个 CustomPrincipal 接口,并使用 IoC (Castle) 创建一个实例并将其传递给控制器​​(随后传递给我的基本控制器)。

基本控制器使用我在 CustomPrincipal 中创建的方法来实现博客作者在 global.asax 中解决的相同任务。即,CustomPrincipal 从数据库或缓存中初始化并分配给 HttpContext.Current.User。

然后,我的控制器/视图可以引用属性,如下所示...

((ICustomPrincipal)(HttpContext.Current.User)).FirstName;

它有效,但我感觉到一些代码味道。首先也是最重要的,如果我从控制器引用 HttpContext,我就终止了单元测试。我正在考虑修改我的 CustomPrincipal 对象以返回上述值(以便我可以在单元测试中模拟它),但我想知道这是否是一种解决方法而不是一个好的解决方案。

我以正确的方式处理这件事吗?我可以做一些小的调整来使其成为一个强大的解决方案,还是应该从头开始使用 FormsAuthenticationTicket 或类似的东西?

谢谢!

I want to be able to access custom properties for an authenticated user like UserId and FirstName without querying the database each time. I found this site through a post on Stack Overflow and I like the approach - but I use IoC / repositories and decided not to try and get global.asax to communicate with the database for fear that it would be incompatible with the repository pattern.

Instead, I created an interface to CustomPrincipal and I use IoC (Castle) to create an instance and pass it to the controllers (and subsequently to my base controller).

The base controller uses methods I created in CustomPrincipal to achieve the same task that the blog author addressed in global.asax. Namely, CustomPrincipal is initialized from the database or cache and assigned to HttpContext.Current.User.

My controllers/views can then reference the properties as follows...

((ICustomPrincipal)(HttpContext.Current.User)).FirstName;

It works, but I'm sensing some code smells. First and foremost, if I reference HttpContext from the controllers I've killed my unit testing. I'm thinking about modifying my CustomPrincipal object to return the above value (such that I can mock it in my unit tests) but I'm wondering if this is a workaround as opposed to a good solution.

Am I going about this the right way? Are there minor tweaks I could do to make it a robust solution or should I start from scratch with FormsAuthenticationTicket or something to that effect?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

洋洋洒洒 2024-10-01 16:43:19

我想提出一个替代想法,以便寻找此信息的人们可以有一些选择。

我去寻找一个可行的 FormsAuthenticationTicket 示例,发现 NerdDinner 在添加自定义属性而不影响单元测试方面做得相当不错。

就我而言,我修改了 LogOn 例程(我已经在单元测试中模拟了)来创建 FormsAuthenticationTicket。 NerdDinner 加密票证并将其添加为 cookie,但我也可以将加密票证添加到缓存,如 原始提案。我还用表示所有自定义属性的 JSON 序列化对象替换了单个 UserData 属性。

CustomIdentityDTO dto = new CustomIdentityDTO { 
   UserId = userId, FirstName = firstName, LastName = lastName };
JavaScriptSerializer serializer = new JavaScriptSerializer();

FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
  1, // version
  username,
  DateTime.Now, // creation
  DateTime.Now.AddMinutes(30), // expiration
  false, // not persistent
  serializer.Serialize(dto));

string encTicket = FormsAuthentication.Encrypt(authTicket);
//HttpContext.Current.Response.Cookies.Add(...)
HttpContext.Current.Cache.Add(username, encTicket, ...

然后,我通过 PostAuthenticateRequest 处理程序在 global.asax 中检索加密票证(从缓存或 cookie),就像 NerdDinner(对于 cookie)或博主的提案(对于缓存)一样。

NerdDinner 实现 IIdentity 而不是 IPrincipal。代码中对自定义字段的引用如下:

((CustomIdentity)Page.User.Identity).FirstName // from partial view

((CustomIdentity)User.Identity).FirstName // from controller

在使用这两种方法之后,我发现 NerdDinner 的方法效果非常好。切换之后我并没有遇到太多的障碍。

I wanted to throw out an alternative idea just so people looking for this information can have some choices.

I went searching for a viable FormsAuthenticationTicket example and found that NerdDinner does a pretty decent job adding custom properties without impacting unit testing.

In my case, I modified my LogOn routine (which I was already mocking in my unit tests) to create a FormsAuthenticationTicket. NerdDinner encrypts the ticket and adds it as a cookie, but I am also able to add the encrypted ticket to cache like the original proposal. I also replaced the single UserData property with a JSON serialized object representing all of my custom properties.

CustomIdentityDTO dto = new CustomIdentityDTO { 
   UserId = userId, FirstName = firstName, LastName = lastName };
JavaScriptSerializer serializer = new JavaScriptSerializer();

FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
  1, // version
  username,
  DateTime.Now, // creation
  DateTime.Now.AddMinutes(30), // expiration
  false, // not persistent
  serializer.Serialize(dto));

string encTicket = FormsAuthentication.Encrypt(authTicket);
//HttpContext.Current.Response.Cookies.Add(...)
HttpContext.Current.Cache.Add(username, encTicket, ...

Then I retrieve the encrypted ticket (either from cache or cookies) in global.asax through a PostAuthenticateRequest handler much like NerdDinner (for cookie) or the blogger's proposal (for cache).

NerdDinner implements IIdentity instead of IPrincipal. References to the custom fields in the code are as follows:

((CustomIdentity)Page.User.Identity).FirstName // from partial view

((CustomIdentity)User.Identity).FirstName // from controller

After working with both methods, I find that NerdDinner's approach works very well. After switching over I haven't encountered much in the way of obstacles.

夏见 2024-10-01 16:43:19

创建 ICustomPrincipalManager 接口怎么样?

public interface ICustomPrincipalManager 
{
    ICustomPrincipal Current {get;}
}

它可以通过访问 HttpContext、数据库、缓存或其他任何内容的类来实现,但您也可以模拟该接口以进行单元测试。您的控制器将使用 IoC 框架来获取 ICustomPrincipalManager,然后访问如下信息:

_customPrincipalManager.Current.FirstName

What about creating an ICustomPrincipalManager interface?

public interface ICustomPrincipalManager 
{
    ICustomPrincipal Current {get;}
}

It can be implemented by a class that accesses HttpContext, the database, caching, or whatever, but you could also mock the interface for unit testing. Your controllers would use the IoC framework to get your ICustomPrincipalManager, and then access information like this:

_customPrincipalManager.Current.FirstName
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文