我正在创建我的第一个 ASP.NET MVC 站点,并且一直在尝试遵循域驱动开发。我的网站是一个项目协作网站,可以将用户分配到该网站上的一个或多个项目。然后,任务将添加到项目中,并且可以将项目中的用户分配给任务。因此,“用户”是我的领域模型的基本概念。
我的计划是拥有一个“用户”模型对象,其中包含有关用户的所有信息,并且可以通过 IUserRepository 进行访问。每个用户都可以通过 UserId 来标识。虽然我现在不确定我是否希望 UserId 是字符串还是整数。
我的域对象 User 和 IUserRepository 应如何与我的网站的更多管理功能(例如授权用户并允许他们登录)相关?如何将我的域模型与 ASP.NET 的其他方面(例如 HttpContext.User、HttpContext.Profile、自定义 MemberShipProvider、自定义 ProfileProvider 或自定义 AuthorizeAttribute)集成?
我应该创建一个自定义的 MembershipProvider 和/或 ProfileProvider 来包装我的 IUserRepository 吗?尽管如此,我也可以预见为什么我可能希望将域模型中的用户信息与站点上用户的授权分开。例如,将来我可能想从表单例份验证切换到 Windows 身份验证。
不要尝试重新发明轮子并坚持使用 ASP.NET 中内置的标准 SqlMembershipProvider 会更好吗?每个用户的个人资料信息将存储在域模型(User/IUserRepository)中,但这不包括他们的密码。然后我会使用标准的 ASP.NET 成员身份来处理用户的创建和授权?因此,在创建帐户或第一次登录时,需要在某个地方有一些代码知道如何在 IUserRepository 中为新用户创建配置文件。
最佳答案
是的 - 非常好的问题。和 @Andrew Cooper 一样,我们的团队也经历了这一切。
我们采用了以下方法(正确或错误):
Custom Membership Provider
我或其他开发人员都不喜欢内置的 ASP.NET 成员(member)资格提供程序。对于我们网站的内容(简单的、UGC 驱动的社交网站)来说,它太臃肿了。我们创建了一个非常简单的程序,它可以满足我们的应用程序的需要,仅此而已。而内置的成员(member)资格提供程序可以满足您可能需要的一切,但很可能不会。
Custom Forms Authentication Ticket/Authentication
我们应用程序中的所有内容都使用接口(interface)驱动的依赖注入(inject)(StructureMap)。这包括表单例份验证。我们创建了一个非常薄的界面:
public interface IAuthenticationService
{
void SignIn(User user, HttpResponseBase httpResponseBase);
void SignOut();
}
这个简单的界面可以轻松进行模拟/测试。通过实现,我们创建了一个自定义表单例份验证票证,其中包含:每个 HTTP 请求所需的 UserId 和 Roles 之类的内容不会经常更改,因此不应在每个请求上获取。
然后,我们使用操作过滤器来解密表单例份验证票证(包括角色)并将其粘贴在 HttpContext.Current.User.Identity
中(为此,我们的主体对象也是基于接口(interface)的) )。
Use of [Authorize] and [AdminOnly]
我们仍然可以利用 MVC 中的授权属性。我们还为每个角色创建了一个。 [AdminOnly]
只是检查当前用户的角色,并抛出 401(禁止)。
Simple, single table for User, simple POCO
所有用户信息都存储在一个表中(“可选”用户信息除外,例如个人资料兴趣)。这被映射到一个简单的 POCO( Entity Framework ),该对象还内置了域逻辑。
User Repository/Service
特定于域的简单用户存储库。例如更改密码、更新配置文件、检索用户等。存储库调用我上面提到的用户对象上的域逻辑。该服务是存储库顶部的一个薄包装器,它将单个存储库方法(例如 Find)分成更专业的方法(FindById、FindByNickname)。
Domain seperated from security
我们的“域”用户及其关联信息。这包括姓名、个人资料、Facebook/社交整合等。
诸如“登录”、“注销”之类的内容正在处理身份验证,而诸如“User.IsInRole”之类的内容则处理授权,因此不属于该域。
因此,我们的 Controller 可以与 IAuthenticationService
和 IUserService
一起使用。
创建配置文件是域逻辑的完美示例,它还与身份验证逻辑混合在一起。
这是我们的样子:
[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.NET 成员(member)资格提供程序那样臃肿而疯狂。它很简单,代表我们的域,而不是 ASP.NET 的表示。
话虽这么说,正如我所说,我们有一个简单的网站。如果您在银行网站上工作,那么我会小心重新发明轮子。
我的建议是先创建您的域/模型,然后再考虑身份验证。 (当然,这就是 DDD 的全部内容)。
然后确定您的安全要求并适当选择身份验证提供程序(现成的或自定义的)。
不要让 ASP.NET 决定您的域应该如何设计。这是大多数人都会陷入的陷阱(包括我,在之前的项目中)。
祝你好运!
关于asp.net - 如何将 "Users"集成到我的 DDD 模型中并验证用户身份?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6867194/