我正在创建我的第一个ASP.NETMVC站点,并且一直在try 跟踪域驱动开发."我的网站"是一个项目协作网站,可以将用户分配到网站上的一个或多个项目.然后将任务添加到项目中,并且可以将项目中的用户分配给任务.因此,"用户"是我的域模型的一个基本概念.

我的计划是拥有一个"用户"模型对象,它包含关于用户的所有信息,可以通过IUserRepository进行访问.每个用户都可以通过一个用户ID来识别.虽然我现在不确定我是想让UserId是字符串还是整数.

我的域对象用户和IUserRepository应该如何与我的站点的更多管理功能(如授权用户并允许他们登录)相关联?如何将我的域模型与ASP的其他方面集成.NET,例如HttpContext.用户,HttpContext.配置文件、自定义成员资格提供程序、自定义配置文件提供程序或自定义属性?

我应该创建一个自定义的MembershipProvider和/或ProfileProvider来包装我的IUserRepository吗?尽管如此,我也可以预见为什么我可能希望将域模型中的用户信息与网站上用户的授权分离.例如,将来我可能想从窗体身份验证切换到windows身份验证.

最好不要try 重新发明轮子,坚持ASP内置的标准SqlMembershipProvider.网每个用户的配置文件信息将存储在域模型(用户/IUserRepository)中,但这不包括他们的密码.然后我会使用标准的ASP.NET成员身份的东西来处理创建和授权用户?因此,在创建新用户的帐户或首次登录时,需要有一些代码知道如何在IUserRepository中为新用户创建配置文件.

推荐答案

是的,非常好的问题.就像"安德鲁·库珀"一样,我们的团队也经历了这一切.

我们采用了以下方法(对或错):

Custom Membership Provider

我和其他开发者都不是内置ASP的粉丝.网络会员Provider .对于我们的网站(简单的、由UGC驱动的社交网站)来说,它太臃肿了.我们创建了一个非常简单的应用程序,可以满足我们的应用程序的需要,仅此而已.而内置的会员服务Provider 可以满足您的所有需求,但很可能不会.

Custom Forms Authentication Ticket/Authentication

我们应用程序中的一切都使用接口驱动的依赖项注入(StructureMap).这包括表单身份验证.我们创建了一个非常薄的界面:

public interface IAuthenticationService
{
   void SignIn(User user, HttpResponseBase httpResponseBase);
   void SignOut();
}

这个简单的界面允许简单的模拟/测试.通过实现,我们创建了一个自定义表单身份验证票证,其中包含:每个HTTP请求都需要用户ID和角色之类的内容,它们不会经常更改,因此不应该在每个请求中提取.

然后,我们使用一个操作过滤器来解密表单身份验证票证(包括角色),并将其粘贴到HttpContext.Current.User.Identity中(我们的主要对象也是基于接口的).

Use of [Authorize] and [AdminOnly]

我们仍然可以使用MVC中的授权属性.我们还 for each 角色创建了一个.[AdminOnly]只判断当前用户的角色,并抛出401(禁止).

Simple, single table for User, simple POCO

所有用户信息都存储在一个表中(除了"可选"用户信息,例如个人资料兴趣).这被映射到一个简单的POCO(实体框架),它也在对象中内置了域逻辑.

User Repository/Service

简单的用户存储库是domain-specific.例如更改密码、更新配置文件、检索用户等.存储库调用我上面提到的用户对象上的域逻辑.该服务是存储库顶部的一个薄包装,它将单个存储库方法(例如Find)分离为更专门的方法(FindById、FindByNickname).

Domain seperated from security

我们的"域",即用户及其关联信息.这包括姓名、个人资料、Facebook/社交整合等.

像"登录"、"注销"这样的内容涉及authentication个,而像"User.IsInRole"这样的内容涉及authorization个,因此不属于该域.

因此,我们的控制器与IAuthenticationServiceIUserService同时工作.

创建概要文件是域逻辑的一个完美例子,它也与身份验证逻辑混合在一起.

下面是我们的

[HttpPost]
[ActionName("Signup")]
public ActionResult Signup(SignupViewModel model)
{
    if (ModelState.IsValid)
    {
        try
        {
            // Map to Domain Model.
            var user = Mapper.Map<SignupViewModel, Core.Entities.Users.User>(model);

            // Create salt and hash password.           
            user.Password = _authenticationService.SaltAndHashPassword();

            // Signup User.
            _userService.Save(user);

            // Save Changes.
            _unitOfWork.Commit();

            // Forms Authenticate this user.
            _authenticationService.SignIn(user, Response);

            // Redirect to homepage.
            return RedirectToAction("Index", "Home", new { area = "" });
        }
        catch (Exception exception)
        {
            ModelState.AddModelError("SignupError", "Sorry, an error occured during Signup. Please try again later.");
            _loggingService.Error(exception);
        }
    }

    return View(model);
}

Summary

以上这些对我们很有效.我有一个简单的用户表,而不是ASP.网络会员Provider .它很简单,代表我们的domain,而不是ASP.NET对它的表示.

That being said个,就像我说的,我们有一个简单的网站.如果你在一家银行网站上工作,那么我在重新发明轮子时会很小心.

我的建议是在考虑身份验证之前,先创建域/模型.(当然,这就是DDD的全部内容).

Then制定出你的安全要求,并 Select 适当的身份验证Provider (现成的或定制的).

不要让ASP.NET指定域的设计方式.这是大多数人都会落入的trap (包括我,在以前的项目中).

祝好运!

Asp.net相关问答推荐

如何在 ASP.NET 中设置自动实现属性的默认值

如何使用 executeReader() 方法仅检索一个单元格的值

Web API Queryable - 如何应用 AutoMapper?

带有 Web 套接字的 SignalR

将 null 转换为字符串的函数

使用 JavaScript 更改 ASP.NET 标签的可见性

如何从 web.config 中读取系统值并在 ASP.NET MVC C# 方法中使用

使用 JSON.NET 解析 json 字符串

使用 gridview asp.net 进行排序和分页

如何将 css 类添加到 ASP.Net 中的更新面板?

Ef core:执行 MaxAsync 时序列不包含任何元素

ASP.NET 日期时间 Select 器

在 ASP.NET 中实现 404 的最佳方法

修改 web.config 时如何防止 ASP.NET 应用程序重新启动?

使用 VirtualPathProvider 从 DLL 加载 ASP.NET MVC 视图

调查 Web.Config 重复部分原因的步骤

如何在 .NET Core 中实现 DbContext 连接字符串?

HttpContext.Current属性的跨线程使用及相关的东西

ASP.NET 中的嵌套中继器

POST 字符串到 ASP.NET Web Api 应用程序 - 返回 null