8

我有以下 ntier 应用程序:MVC > 服务 > 存储库 > 域。我正在使用表单身份验证。在我的 MVC 层之外使用 Thread.CurrentPrincipal 来获取我的应用程序的当前登录用户是否安全,或者我应该使用 HttpContext.Current.User 吗?

我问的原因是 Thread.CurrentPrincipal 似乎存在一些问题,但我谨慎地在 MVC 层之外添加对 System.Web 的引用,以防我将来需要提供非 Web 字体端。

更新

到目前为止,我一直在遵循收到的建议,将用户名作为被调用方法的参数的一部分传递到服务中,这导致了对我最初问题的改进。我需要能够检查用户是否在我的许多服务和域方法中处于特定角色。似乎有几个解决方案,只是想知道哪个是最好的方法:

  1. 将整个 HttpContext.Current.User 作为参数传递,而不仅仅是用户名。
  2. 在我的网络层之外调用 Thread.CurrentPrincipal 并使用它。但是如何确保它等于 HttpContext.Current.User?
  3. 坚持按照目前的建议传递用户名,然后使用 Roles.IsUserInRole。这种方法的问题是它需要一个对 System.Web 的引用,我觉得这在我的 MVC 层之外是不正确的。

你会建议我如何进行?

4

5 回答 5

2

我也不会这样做,HttpContext.Current.User特定于您的 Web 层。

为什么不将用户名注入您的服务层?

于 2012-07-18T14:39:36.463 回答
2

您应该抽象您的用户信息,使其不依赖于Thread.CurrentPrincipalor HttpContext.Current.User

例如,您可以添加接受用户名的构造函数或方法参数。

这是构造函数参数的过度简化示例:

class YourBusinessClass 
{
   string _userName;
   public YourBusinessClass(string userName)
   {
      _userName = userName;
   }

   public void SomeBusinessMethodThatNeedsUserName()
   {
      if (_userName == "sally")
      {
         // do something for sally
      }
   }
}
于 2012-07-18T14:39:58.403 回答
2

将相关的用户详细信息映射到新Class的以表示 LoggedInUser 并将其作为参数传递给您的业务层方法

 public class LoggedInUser
 {
   public string UserName { set;get;}
   //other relevant proerties
 }

现在设置它的值并传递给你的 BL 方法

var usr=new LoggedInUser();
usr.UserName="test value ";  //Read from the FormsAuthentication stuff and Set
var result=YourBusinessLayerClass.SomeOperation(usr);
于 2012-07-18T14:41:42.973 回答
2

我更喜欢选项 2(在 Web 层之外使用 Thread.CurrentPrincipal )。因为这不会影响您的服务层和数据层方法。附带奖金:您可以将您的角色 + 附加信息存储在自定义主体中;

确保您的服务和数据层中的 Thread.CurrentPrincipal 与您的 Web 层相同;您可以在 Global.asax(Application_AuthenticateRequest) 中设置您的 HttpContext.Current.User (Context.User)。您可以设置的其他替代位置添加在底部。

示例代码:

    //sample synchronizing HttpContext.Current.User with Thread.CurrentPrincipal
    protected void Application_AuthenticateRequest(Object sender, EventArgs e)
    {
        HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];

        //make sure principal is not set for anonymous user/unauthenticated request
        if (authCookie != null && Request.IsAuthenticated)
        {
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

            //your additional info stored in cookies: multiple roles, privileges, etc
            string userData = authTicket.UserData;

            CustomPrincipal userPrincipal = PrincipalHelper.CreatePrincipal(authTicket.Name, authTicket.UserData, Request.IsAuthenticated);

            Context.User = userPrincipal;
        }
    }

当然,首先您必须实现您的登录表单以创建包含您的自定义主体的授权 cookie。

Application_AuthenticateRequest 将对服务器的任何请求(css 文件、javascript 文件、图像文件等)执行。要将此功能仅限于控制器操作,您可以尝试在 ActionFilter 中设置自定义主体(我没有尝试过)。我尝试的是在控制器的拦截器中设置此功能(我使用 Castle Windsor 进行依赖注入和面向方面的编程)。

于 2012-08-29T04:55:34.463 回答
1

我相信您遇到了这个问题,因为您需要进一步限制您的域责任。您的服务或文件不应该负责处理授权。该责任应由您的 MVC 层处理,因为当前用户登录到您的 Web 应用程序,而不是您的域。

如果不是尝试从您的服务或文档中查找当前用户,而是在您的 MVC 应用程序中执行检查,您会得到如下内容:

if(Roles.IsUserInRole("DocumentEditorRole")){

    //UpdateDocument does NOT authorize the user. It does only 1 thing, update the document.
    myDocumentService.UpdateDocument(currentUsername, documentToEdit);

} else {

    lblPermissionDenied.InnerText = @"You do not have permission 
                                      to edit this document.";

}

它干净,易于阅读,并允许您使您的服务和域类免于授权问题。您仍然可以映射Roles.IsUserInRole("DocumentEditorRole")到您的视图模型,因此您唯一丢失的是 Document 类上的 CurrentUserCanEdit 方法。但是,如果您认为您的域模型代表真实世界的对象,那么该方法无论如何都不属于 Document。您可能会将其视为域 User 对象 ( ) 上的一种方法user.CanEditDocument(doc),但总而言之,我认为如果您将授权排除在域层之外,您会更开心。

于 2013-01-07T06:07:09.593 回答