更新其他用户的会话 - 最佳做法?

时间:2010-12-16 18:11:30

标签: php codeigniter

与许多Web应用程序一样,当我的用户登录我的站点时,我设置了几个控制访问站点功能权限的会话变量。

每当用户执行可能导致更改这些权限的操作时,我都会将会话变量更新为正常处理流程的一部分。

但有时需要根据其他用户的操作更新用户的会话。例如,主持人升级用户的权限。主持人无权访问用户的会话空间,因此无法为受影响的用户运行正常的更新功能。

我考虑了一些强制更新到另一个用户会话的方法,但它们都有缺点:

  • 我可以搜索并删除他们的 会话表中的会话,但是 这需要全表扫描 (其中sessdata如'%user_id%'), 它可能导致损失 受影响的用户可能的内容 从事发电。
  • 我可以强制更新会话 定期,例如何时 sess_time_to_update被触发。 但不能保证这一点 将在用户之前发生 试图访问 更新的功能 需要的。
  • 我可以运行一系列完整的 每个页面加载的变量更新, 但保持一个整体的重点 会话是为了避免这种开销。
  • 我可以设置一个表示需要的标志 用于会话更新,但该信号 必须在每个页面执行和每个控制器中查询。 (我知道我可以将CI_Controller扩展为MY_Controller,但我不想这样做)
  • 我已经向用户发送了一封电子邮件 通知他们的变化 许可,这很容易 让他们点击会话更新链接 (甚至退出并重新登录)。 但不能保证他们是 甚至超越主题阅读 行,更别说实际点击了 链接。

那么我有什么遗漏的吗?我在寻找一个不存在的魔法子弹吗?

(请注意,我已使用CodeIgniter对此进行了标记,并参考了CI特定的技术细节,因为这就是我正在使用的内容。也就是说,这不是特定于CI的(甚至是特定于PHP的) )问题。)

感谢您的帮助!

4 个答案:

答案 0 :(得分:3)

嗯,一个选项是设置“ACL版本”号(适用于所有用户或每个用户)。然后在初始化会话时(好吧,当您调用session_start()时)检查存储的版本是否与会话的版本匹配。如果没有,请刷新ACL。

另一种稍微不同的方法是在会话表中添加一列(“status”或“dirty”等)。然后,只需将该状态更新为1,而不是终止会话。然后在加载会话时,检查该标志以查看它是否为1.如果是,请重新加载会话中的缓存数据并将其设置为0.如果不是,请继续...

至于更新另一个用户的会话,我不会这样做...如果会话由PHP管理,那么你需要终止当前会话并启动你想要修改的会话。这只是问问题......(你需要这样做,因为PHP的机制不使用序列化。)。

至于删除会话,我不担心全表扫描。除非你有TON用户一直在更新权限,否则不重要。数据丢失是一个重要的问题,所以那个想法的缺点......

至于其他两个选项,我认为你的头上钉了一针,所以我没有别的东西可以提供给他们......

答案 1 :(得分:2)

虽然我的方法并不完全适用于Code Igniter,但我会采取刺激措施。

我过去解决这个问题的方法是使用构造函数创建一个User对象模型,该构造函数从存储凭据的数据库主键中获取UserID。我将编写一个静态登录方法来检查登录凭据,然后实例化并返回一个用户实例,如果登录对某行是正确的,然后设置会话。

到目前为止这么好,对吗?因此,您的所有权限,访问级别等都存储在数据库中。就像使用登录方法一样,我们可以使用刷新方法重新实例化对象,从已经获得的主键中重新获取数据库。

class User{
public function __construct($uid){
  //fetch from the db here
  $sql = 'SELECT FROM User_Table WHERE UserID = ?';
  $params = array($uid);
  //fetch and assign using your flavor of database access, I use PDO
  //set all your object properties for access, as well as user_id, something like 
  //$this->user_id = $result['UserID'];
}

public static function Login($uname, $pass){
  $sql = 'SELECT UserID FROM User WHERE Username = ? AND Password = ?';
  $params = array($uname, md5($pass));
  //again I'm going to employ pseudocode here, fetch according to your preferred system
  if(!empty($result)){
    $_SESSION['user'] = new User($result['UserID']);
  }else{
     //login failed!
     return false;   
  }
}

final public function _refresh(){
  //refresher method.  Since the controller sets all the object properties for access
  //reconstructing and assigning it refreshes these priveliges.
  $_SESSION['user'] = new User($this->user_id);
  }

}

使用此模型,每当我在会话中对可能需要时间敏感权限的用户执行操作时,我都可以在会话中为用户对象调用refresh。假设我们有一个控制器功能来访问受限制的表单。

function _topSecret(){
$_SESSION['user']->refresh();

if($_SESSION['user']->specific_permission_from_db){
  //Perform the special action, get the view, whatever.
}else{
  //redirect to an error page
}

}

因此,如果您已经为管理员编写了例程,他们需要做的就是在数据库中设置用户的权限,当刷新方法在特定的时间敏感函数上运行时,该数据将被放入用户会话。因此,您不一定要更新另一个用户的会话,您只需确定哪些操作按时间顺序敏感,它们需要最新权限并将该命令发送给会话中的用户对象。

这对我来说已经证明非常有效,因为重建只需要在需要最新权限的操作上执行。同样,我不确定它在CI和您的个人应用程序中是否有用,但我想我会分享。我假设你已经完成了所有必要的会话安全措施并开始了会话 - 像CI这样的大多数框架都会通过他们的本地方式为你处理这个问题。

答案 2 :(得分:1)

在会话表中添加一个可以保存用户对象主键的列。在登录时设置它,稍后使用它来更新会话。

编辑:如果您不想扩展会话表,请创建一个其他查找表,其中包含用户对象ID和会话密钥链接。

答案 3 :(得分:0)

如果您将会话存储为本机php会话,我倾向于不管它,并允许他们获得某种通知,他们需要登录/注销以刷新设置。我也有会话数据存储在数据库中的站点,然后更简单地写入新设置。