时间:2011-10-26 16:13:00

标签: c# asp.net sql-server tsql

这对我来说很难解释,但我尽力做到最好。

我有一个具有登录权限的多个用户的应用程序。这些用户可以附加一些员工。我希望避免用户查看/编辑不属于他们的其他员工。

我有一个用户类,如下所示:

public class User
{
    public int ID { get; set; }
    public string Name { get; set; }

    public User()
    {
    }

    public User(int userid)
    {
        //
        // Gets the user from the database and fills the properties
        //
    }
}

这样的员工类:

public class Employee
{
    public int ID { get; set; }
    public string Name { get; set; }

    public Employee()
    {
    }

    public Employee(int employeeid)
    {
        //
        // Gets the employee from the database and fills the properties
        //
    }
}

这个问题就像编辑员工时应用程序中的查询字符串一样:

  

〜/ EditEmployee.aspx?ID = 1

id是员工的ID。通过快速编辑此ID,我很幸运能够获取不属于当前登录用户的员工。

虽然这可以通过

来解决
public Employee GetEmployee(int id) 
{
    // Gets the employee (using this.ID as UserID) from the database
}
User对象上的

方法,它为存储过程提供User.ID属性并检查:

SELECT * FROM EmployeeTable WHERE EmployeeID = @EmployeeID AND UserID = @UserID

但有了这个,我总是必须创建一个User对象的实例来获得一个雇员。

这会使Employee(int id)对象上的Employee过时。

还有其他方法吗?

这个问题是我处于这样的情况:我不想让User对象的实例获得雇员,因为我100%确定我拥有正确的员工ID。 (避免过多的数据库调用)。

我是否真的必须将Employee(int id)保留在Employee对象上并创建一个不会检查UserID的新存储过程?

这个例子是虚构的。解释它的最好方法莫过于粘贴数百行代码和对象。也许我太过于表现怪胎了。但我只想改进我做多个用户网站的方式。

我真的希望这能解释得很好,我尽我所能。 : - )

5 个答案:

答案 0 :(得分:6)

我认为这里最基本的问题是你正在将你的代码/对象实例与你的DataAccess混合。

我建议你分开两个。不要使用对象的构造函数作为从数据库中检索数据的方法。有一个单独的DataAccess层来执行查询,然后给你的员工对象加水。

如果员工数据只能在当前用户的上下文中检索,那么您需要: a)在您的网络会话期间保持该userId可用 b)将userId作为过滤器传递给每个数据库查询,以确保只返回正确的数据。

  • 所以只需在开始时创建一次User对象。 (即他们登录时)
  • User对象放入Session / Cache
  • 每次进入数据库时​​,将UserId以及EmployeeId传递给Query。

修改

只是强调,我没有建议将会话变量混合到您的DataAccess中。 您可以在Page / BasePage上拥有一些属性,用于在会话中存储用户对象。 e.g。

public User CurrentUser {
    get { 
        return Session["CurrentUser"] != null ?
            (User) Session["CurrentUser"] :
            null;
    }
    set { Session["CurrentUser"] = value; }
}

当用户登录时,您将使用用户对象填充该对象。然后您可以随时返回并获取它。 e.g。

protected void Page_Load(object sender, EventArgs e)
{
     var empId = //Code to get it from Query String.
     var userId = this.CurrentUser.UserId;
     Employee e = DataAccess.GetEmployee(empId, userId);
     if(e != null) 
         //Do funky stuff
}

答案 1 :(得分:1)

我故意排除任何角色,权限等的提及。这是一个简单的方法(也许)。

我没有将原始行ID传递给查询字符串,而是使用GUID或其他唯一ID代表员工,并将其传递给员工。这种方式无法猜到。然后,您还可以拥有可以测试的用户类中包含的员工ID(不是原始ID)列表,如果用户想要查看员工,则检查查询字符串ID以查看用户是否有权查看该员工基于员工身份的内部表示。

答案 2 :(得分:0)

从评论中确定您担心有人编辑查询字符串。我喜欢@Frank的建议但是......

您可以先验证请求,然后显示页面!检查并查看用户是否使用会话对象登录,然后只显示该页面。

然后可能将您的方法更改为:

public Employee GetEmployeeForUser(int employeeid, int userId)
{
} 

所以你得到了合适的员工。

也许你过度优化/思考你的设计和/或性能的正确性。有时你放置一些简单的方法,使应用程序工作,然后你可以重新考虑和性能调整,这无关紧要。

答案 3 :(得分:0)

您是否也不能将UserID提供给EditEmployee.aspx页面?像〜/ EditEmployee.aspx?id = 1那样的Session变量包含用户ID?当用户访问EditEmployee.aspx页面时,您必须知道UserID是什么(否则您将如何知道该Employee是否属于该用户?)。

由于您必须有某种方法来了解该信息,为什么不修改您的构造函数以包含UserID?我认为这样做是完全有效的,看看你的Employee类在决定是否“允许”加载数据之前需要关于User类的一些信息......

也许是这样的?

public class Employee
{
    // .... other things

    public Employee(int userid, int employeeid)
    {
        //
        // Gets the employee from the database and fills the properties
        //
        //SELECT * FROM EmployeeTable WHERE EmployeeID = @EmployeeID AND UserID = @UserID
    }
}

答案 4 :(得分:0)

您可能有某种方式知道当前登录用户是谁。这可以是从完整用户对象到存储在会话中的用户标识或用户名的任何内容。

然后我建议使用某种工厂方法来获取员工(类似于您的示例,但不是用户对象)。如果当前登录的用户实际上无权访问该员工,则此方法可以执行安全检查并抛出适当的安全性异常(或其他)。

最重要的是,您必须在某个时刻一起查看userid和employeeid,以确定是否授予了权限。关键是你不需要一个完整的对象,只需要一小部分识别信息。