Asp.net mvc 在Web层之外获取当前主体
我有以下ntier应用程序:MVC>服务>存储库>域。我正在使用表单身份验证。在MVC层之外使用Thread.CurrentPrincipal获取当前登录的应用程序用户安全吗?还是应该使用HttpContext.Current.user 我问的原因是Thread.CurrentPrincipal似乎存在一些问题,但我谨慎地在MVC层之外添加对System.Web的引用,以防将来需要提供非Web字体端 更新 我一直遵循到目前为止收到的建议,将用户名作为被调用方法的参数的一部分传递到服务中,这导致了我原来问题的改进。我需要能够检查用户是否在我的许多服务和域方法中扮演特定角色。似乎有几种解决方案,只是想知道哪种方法是最好的:Asp.net mvc 在Web层之外获取当前主体,asp.net-mvc,asp.net-mvc-3,authentication,forms-authentication,iprincipal,Asp.net Mvc,Asp.net Mvc 3,Authentication,Forms Authentication,Iprincipal,我有以下ntier应用程序:MVC>服务>存储库>域。我正在使用表单身份验证。在MVC层之外使用Thread.CurrentPrincipal获取当前登录的应用程序用户安全吗?还是应该使用HttpContext.Current.user 我问的原因是Thread.CurrentPrincipal似乎存在一些问题,但我谨慎地在MVC层之外添加对System.Web的引用,以防将来需要提供非Web字体端 更新 我一直遵循到目前为止收到的建议,将用户名作为被调用方法的参数的一部分传递到服务中,这导致了
您建议我如何继续?我也不会这样做,
HttpContext.Current.User
特定于您的web层
为什么不将用户名注入您的服务层?您应该抽象您的用户信息,以便它不依赖于
线程.CurrentPrincipal
或HttpContext.Current.user
例如,您可以添加接受用户名的构造函数或方法参数
下面是一个过度简化的构造函数参数示例:
class YourBusinessClass
{
string _userName;
public YourBusinessClass(string userName)
{
_userName = userName;
}
public void SomeBusinessMethodThatNeedsUserName()
{
if (_userName == "sally")
{
// do something for sally
}
}
}
将相关用户详细信息映射到新的
类
,以表示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);
我更喜欢选项2(在web层之外使用Thread.CurrentPrincipal)。因为这不会影响您的服务层和数据层方法。奖金:您可以在自定义主体中存储您的角色+附加信息
确保服务和数据层中的Thread.CurrentPrincipal与web层相同;您可以在Global.asax(Application\u 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文件、图像文件等)执行应用程序_AuthenticateRequest。要将此功能仅限于控制器操作,可以尝试在ActionFilter中设置自定义主体(我还没有尝试过)。我尝试的是在控制器的拦截器中设置此功能(我使用Castle Windsor进行依赖注入和面向方面的编程)。我认为您遇到了这个问题,因为您需要进一步限制您的域责任。您的服务或文档不应负责处理授权。这个责任应该由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.";
}
它干净、易于阅读,并允许您保持您的服务和域类不受授权问题的影响。您仍然可以将
角色.IsUserInRole(“DocumentEditorRole”)
映射到您的viewmodel,因此唯一丢失的是Document类上的CurrentUserCanEdit方法。但是,如果您认为域模型代表真实世界的对象,那么该方法无论如何都不属于文档。您可能认为它是域用户对象(User.CanEditDocument(doc)
)上的一种方法,但总而言之,我认为如果您将授权排除在域层之外,您会更高兴。您的意思是创建我自己的IPrincipal实现吗?在成功登录期间是否会设置此选项?如果是这样,我将如何在业务层中检索它?不,只检索业务层中需要的内容。假设您只需要用户名,您可以执行类似于我在答案中添加的示例的操作。Shyju、Joe R和我基本上都在说同样的话,只给你的业务层它需要的东西。同意,我们真的在说同样的话。:-)所以我认为Thread.CurrentPrincipal不应该被使用是对的?我在域对象(如CanEdit)上有许多方法,这些方法会对此进行检查,以确保用户经过身份验证。这工作得很好,因为我可以自动将结果映射到Viewmodel上的bool。如果从我的域中删除Thread.CurrentPrincipal,我似乎需要做一些大的更改!因此,我可以将HttpContext.Current.User注入到我的服务中,而该服务在ctor中接受IPrincipal?将通过HttpContext.Current.User填充的用户名传递到服务层。它可以是字符串参数,如果需要更多值,甚至可以是新的对象参数。所以不,不是我的校长。是的,在成功登录后。如果您删除Thread.CurrentPrincipal并使用您自己的POCO,您将拥有更大的灵活性,更改将更容易。另一方面,您必须替换现有代码,这些代码可能适合您相对简单的需求。您必须根据项目动态来决定采取哪条路径,如果您完成后更新页面,这样我们就可以看到它是如何进行的,这将是一件好事。我仍在研究这一点。我面临的问题是,