如何将 DDD 模型中的"Users"与身份验证用户集成?



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

我的计划是有一个"User"模型对象,它包含关于用户的所有信息,可以通过IUserRepository访问。每个用户都可以通过UserId来标识。虽然我不确定在这一点上,如果我想UserId是一个字符串或整数。

我的域对象User和IUserRepository应该如何与我的站点的更多管理功能相关联,比如授权用户和允许他们登录?我如何将我的领域模型与ASP的其他方面集成?. NET如HttpContext。用户,HttpContext。配置文件、自定义MemberShipProvider、自定义ProfileProvider还是自定义AuthorizeAttribute?

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

不要尝试重新发明轮子,坚持使用标准的SqlMembershipProvider内置到ASP.NET中会更好吗?每个用户的概要信息将存储在域模型中(user/IUserRepository),但这将不包括他们的密码。然后我会使用标准的ASP。. NET成员的东西来处理创建和授权用户?因此,需要在某个地方编写一些代码,以便在IUserRepository中创建新用户的配置文件,当他们创建帐户或第一次登录时。

是的,非常好的问题。和@Andrew Cooper一样,我们的团队也经历了这一切。

我们采用了以下方法(正确或错误):

自定义会员提供程序

我和其他开发人员都不是内置ASP的粉丝。. NET成员资格提供程序。对于我们的网站(一个简单的、受ugc驱动的社交网站)来说,这太臃肿了。我们创建了一个非常简单的对象,它只满足应用程序的需要,仅此而已。而内置的成员资格提供程序可以完成可能需要但很可能不需要的一切。

自定义表单认证票/认证

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

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

这个简单的接口允许简单的模拟/测试。通过实现,我们创建了一个自定义表单身份验证票据,其中包含:每个HTTP请求都需要的UserId和Roles之类的东西,它们不会经常更改,因此不应该在每个请求中获取。

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

[authorization]和[AdminOnly]的使用

我们仍然可以在MVC中使用授权属性。我们还为每个角色创建了一个。[AdminOnly]只是检查当前用户的角色,并抛出401(禁止)。

Simple, single table for User, Simple POCO

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

用户库/服务

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

与安全分离的域

我们的"域名"是指用户及其关联信息。这包括姓名、个人资料、facebook/社交整合等。

诸如"Login","Logout"之类的东西处理身份验证和诸如"User"之类的东西。IsInRole"处理授权,因此不属于域。

所以我们的控制器可以与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);
}

以上对我们来说很有效。我喜欢有一个简单的User表,而不是像ASP那样臃肿的疯狂。. NET成员资格提供程序。它很简单,代表我们的,而不是ASP。. NET对它的表示

也就是说,正如我所说,我们有一个简单的网站。如果你在做一个银行网站,那么我会小心不要重复发明轮子。

我的建议是先创建你的域/模型,然后再考虑身份验证。(当然,这就是DDD的全部内容)。

然后计算出您的安全需求并选择适当的身份验证提供者(现成的或自定义的)。

不要让ASP。. NET规定了您的域应该如何设计。这是大多数人都会掉进的陷阱(包括我,在之前的项目中)。

祝你好运!

让我来分析一下你的问题:

虽然我不确定在这一点上,如果我想UserId是一个字符串或整数。

不一定是整数,但一定要使用某种基于位的值(例如int, long或guid)。在固定大小值上操作的索引比在字符串上操作的索引快得多,并且在您的生命周期中,您永远不会用完用户的标识符。

我的域对象User和IUserRepository应该如何与我的站点的更多管理功能相关联,比如授权用户和允许他们登录?

决定是否要使用内建的asp.net成员。我不建议这么做,因为它太臃肿了,而且你必须自己实现它的大部分功能,比如电子邮件验证,从生成的表格来看,你可能会认为它是内置的……模板项目的ASP。. NET MVC 1和. NET MVC 2都包含一个简单的成员关系存储库,只需重写实际验证用户的函数,您就可以顺利完成。

我如何将我的领域模型与ASP的其他方面集成?. NET如HttpContext。用户,HttpContext。配置文件、自定义MemberShipProvider、自定义ProfileProvider还是自定义AuthorizeAttribute?

每一个都值得有自己的SO问题,每个问题之前都被问到过。也就是说,HttpContext。用户只有在使用内置的FormsAuthentication功能时才有用,我建议在开始时使用它,直到遇到它不做你想做的事情的情况。我喜欢在使用FormsAuthentication登录时将用户密钥存储在名称中,如果HttpContext.User.IsAuthenticatedtrue,则在每个请求开始时加载请求绑定的当前用户对象。

至于配置文件,我极力避免有状态请求,而且以前从未使用过它,所以必须由其他人来帮助您。

使用内置的[Authorize]属性所需要做的就是告诉FormsAuthentication用户已被验证。如果您想使用authorize属性的角色特性,那么编写您自己的RoleProvider,它将像魔术一样工作。你可以在Stack Overflow上找到很多这样的例子。HACK:你只需要实现RoleProvider.GetAllRoles(), RoleProvider.GetRolesForUser(string username)RoleProvider.IsUserInRole(string username, string roleName)就可以让它工作。除非您希望使用asp.net成员系统的所有功能,否则您不必实现整个接口。

不要尝试重新发明轮子,坚持使用标准的SqlMembershipProvider内置到ASP.NET中会更好吗?

对于这个问题的每一个推导,务实的答案都是不要重新发明轮子,直到轮子不能做你需要它做的事,你需要它怎么做。

if (built in stuff works fine) {
    use the built in stuff;  
} else {
    write your own;
}
if (easier to write your own then figure out how to use another tool) {
    write your own;
} else {
    use another tool;
}
if (need feature not in the system) {
    if (time to extend existing api < time to do it yourself) {
        extend api;
    } else {
        do it yourself;
    }
}

我知道我的回答有点晚了,但为了将来参考其他有同样问题的同事。

下面是一个使用角色的自定义身份验证和授权的示例。http://www.codeproject.com/Articles/408306/Understanding-and-Implementing-ASP-NET-Custom-Form。这是一篇非常好的文章,非常新鲜,而且是最近的。

在我看来,您应该将此实现作为基础设施的一部分(只需创建一个新项目Security或任何您想要称呼它的名称)并实现上面的示例。然后从您的应用层调用该机制。请记住,应用层控制和编排应用程序中的整个操作。领域层应该专门关注业务操作,而不是访问或数据持久性等。它不知道如何对系统中的人员进行身份验证。

想想一家实体公司。实施的指纹门禁系统与该公司的运营无关,但它仍然是基础设施(建筑)的一部分。事实上,它控制着谁可以进入公司,这样他们就可以履行各自的职责。你没有两个雇员,一个扫描他的指纹,另一个进去做自己的工作。您只有一个拥有食指的Employee。要想"进入",你只需要他的手指……因此,如果您打算使用相同的UserRepository进行身份验证,那么您的存储库应该包含一个用于身份验证的方法。如果您决定使用AccessService(这是一个应用程序服务,而不是域服务),则需要包含UserRepository,以便访问该用户数据,获取他的手指信息(用户名和密码),并将其与来自表单的任何信息(手指扫描)进行比较。我解释得对吗?

大多数DDD的情况适用于现实生活中的情况…当涉及到软件的架构时

相关内容

最新更新