获取Web层之外的当前Principal

时间:2012-07-18 14:37:25

标签: asp.net-mvc asp.net-mvc-3 authentication forms-authentication iprincipal

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

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

更新

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

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

5 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:2)

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

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

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

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

   public void SomeBusinessMethodThatNeedsUserName()
   {
      if (_userName == "sally")
      {
         // do something for sally
      }
   }
}

答案 2 :(得分: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);

答案 3 :(得分: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。

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

答案 4 :(得分:1)

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

如果您不是尝试从服务或文档中查找当前用户,而是在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")映射到您的viewmodel,因此您唯一丢失的是Document类中的CurrentUserCanEdit方法。但是,如果您认为您的域模型代表真实世界对象,那么该方法无论如何都不属于Document。您可能会将其视为域用户对象(user.CanEditDocument(doc))上的一种方法,但总而言之,如果您将授权保留在域层之外,我认为您会更高兴。