高效会话与数据持久化解决方案详解

更新:11-08 民间故事 我要投稿 纠错 投诉

大家好,今天小编来为大家解答以下的问题,关于高效会话与数据持久化解决方案详解,这个很多人还不知道,现在让我们一起来看看吧!

使用会话跟踪

我们可以使用session模块来跟踪用户,如下例所示:

会话开始();

if (!isset($_SESSION["访问"])){

$_SESSION["访问"]=0;

}

$_SESSION["访问"]++;

echo "您已经访问过这里".$_SESSION["visit"]."次。会话模块通过向用户发送cookie来跟踪用户。该cookie包含一个随机生成的会话id,该cookie被命名为PHPSESSID。如果用户不接受cookies,URL后面会添加什么? PHPSESSID=xxxx(id) 这样就可以传递到下一页了。显然这样的URL是不安全的。例如,如果用户复制URL并将其发送给其他人,那么其他人会无意中冒充该用户并访问该网站,因此默认情况下禁止这种行为。要启用在URL 中传递会话id 的功能,您可以在启动会话之前使用ini_set("session.use_trans_sid",true) 。

防止会话劫持

为了确保攻击者无法访问其他用户的会话,我们可以指定只允许会话id通过cookie传递,并生成另一个会话令牌通过URL传递。只有包含有效会话id 和有效令牌的会话才能被访问,如以下示例代码所示:

ini_set("session.use_only_cookies", true);

//指定客户端是否只使用cookie来存储会话ID。启用此设置可以防止与通过URL 传递会话ID 相关的攻击。

会话开始();

$salt="YourSpecialValueHere";

$tokenstr=strval(日期("W")).$salt;

$token=md5($tokenstr);

if (!isset($_REQUEST["token"]) || $_REQUEST["token"] !=$token){

//提示登录

echo "请登录";

出口;

}

$_SESSION["token"]=$token;

output_add_rewrite_var("token", $token);此示例通过将当前周数strval(date("W")) 和变量$salt 连接到字符串中来创建自动移位的标记,以确保标记不固定。而且在一段时间内是合法的。

然后检查请求中的token【$_REQUEST具有$_POST和$_GET的功能,但是比较慢】。如果没有找到,会提示您重新登录。如果找到则添加到生成的链接中【比如当前页面标签】链接后作为get的参数使用】或者在表单中【以输入隐藏字段的形式】保证下次请求可以顺利进行。使用output_add_rewrite_var()来实现上述功能。

防止会话固定攻击

为了确保应用程序不易受到会话固定攻击(攻击者强迫用户使用预定义的会话ID),我们应该使用会话cookie,但会话标识符不会附加到URL,并且新的会话ID 会被添加到URL 中。频繁生成。

ini_set("session.use_only_cookies", true);

会话开始();

if (!isset($_SESSION["生成"])

|| $_SESSION["生成"] (time() - 30)) {

session_regenerate_id();

$_SESSION["生成"]=time();

}这个例子首先设置了session行为,即只能使用cookie来存储session id,以确保PHP不会关注攻击者放置在URL中的session id。

一旦会话开始,设置一个值来记录上次生成会话ID的时间,并定期生成新的会话ID。本例中设置的时间为30秒,可以大大降低攻击者获得合法会话ID的机会。

这两种方法结合起来可以基本消除会话固定攻击的风险。由于会话ID经常变化,攻击者很难获得合法的会话ID。此外,由于会话ID只能在cookie中传递,因此不可能进行基于URL的攻击。

在数据库中存储会话

我们可能希望将会话数据存储在数据库中而不是文件中。在这种情况下,如果多个服务器可以访问同一个数据库,则会话数据将被镜像到所有Web服务器。具体方法是通过向session_set_save_handler()提供一个实现SessionHandlerInterface接口的实例来注册一个自定义的session存储函数(这个只能在PHP 5.4之后的版本中使用)。首先,我们实现接口如下。它的文件名为db.php,它使用PDO 将会话数据存储在数据库表中:

DBHandler 类实现SessionHandlerInterface {

受保护的$dbh;

/**

* 打开回调函数类似于类的构造函数,会在会话打开时被调用。

* 这是自动启动会话或通过调用session_start()手动启动会话后调用的第一个回调函数。

* 该回调函数如果操作成功则返回TRUE,否则返回FALSE。

*/

公共函数打开($保存路径,$名称){

尝试{

$this-connect($save_path, $name);

返回真;

} catch (PDOException $e) {

返回假;

}

}

/**

* close回调函数类似于类的析构函数。调用write 回调函数后调用。

* 调用session_write_close()函数后,关闭回调函数也会被调用。

* 该回调函数如果操作成功则返回TRUE,否则返回FALSE。

*/

公共函数关闭(){

返回真;

}

/**

* 销毁session时会被调用

* 当调用session_destroy()函数,或者调用session_regenerate_id()函数并且destroy参数设置为TRUE时,会调用该回调函数。

* 该回调函数如果操作成功则返回TRUE,否则返回FALSE。

*/

公共函数销毁($session_id){

$sth=$this-dbh-prepare("从会话中删除session_id=?");

$sth-execute(数组($session_id));

返回真;

}

/**

* 读取会话时调用

* 如果会话中有数据,读取回调函数必须返回一个对会话数据进行编码(序列化)的字符串。如果会话中没有数据,则读取回调函数返回空字符串。

*

* 通过调用session_start()函数自动或手动启动会话后,PHP内部会调用read回调函数来获取会话数据。在调用read之前,PHP会调用open回调函数。

*

* 读回调返回的序列化字符串的格式必须与写回调函数保存数据时的格式完全相同。 PHP 将自动反序列化返回的字符串并填充$_SESSION 超级全局变量。尽管数据看起来与serialize()函数非常相似,但重要的是要记住它们是不同的。

*/

公共函数读取($session_id){

$sth=$this-dbh-prepare("从会话中选择session_data,其中session_id=?");

$sth-execute(数组($session_id));

$rows=$sth-fetchAll(PDO:FETCH_NUM);

if (count($rows)==0) {

返回"";

} 别的{

返回$行[0][0];

}

}

/**

* 将数据写入数据库

*/

公共函数write($session_id, $session_data) {

$现在=时间();

$sth=$this-dbh-prepare("更新会话SET session_data=?last_update=? WHERE session_id=?");

$sth-execute(数组($session_data, $now, $session_id));

if ($sth-rowCount()==0) {

$sth2=$this-dbh-prepare("插入会话(session_id,session_data,last_update) VALUES (?)");

$sth2-execute(数组($session_id, $session_data, $now));

}

}

/**

* 创建表

*/

公共函数createTable($save_path, $name, $connect=true) {

如果($连接){

$this-connect($save_path, $name);

}

$sql=_SQL_

创建表会话(

session_id VARCHAR(64) NOT NULL,

session_data MEDIUMTEXT NOT NULL,

最后更新时间戳不为空,

主键(会话ID)

_SQL_;

$this-dbh-exec($sql);

}

/**

* 连接到数据库

*/

受保护的函数连接($save_path){

/* 在DSN 中查找用户和密码作为“查询字符串”参数*/

$parts=parse_url($save_path);

if (isset($parts["query"])) {

parse_str($parts["query"], $query);

$user=isset($query["user"]) ? $query["用户"] : null;

$password=isset($query["password"]) ? $查询["密码"] : null;

$dsn=$parts["scheme"] 。 ":";

if (isset($parts["host"])) {

$dsn .="//" 。 $parts["主机"];

}

$dsn .=$parts["路径"];

$this-dbh=new PDO($dsn, $user, $password);

} 别的{

$this-dbh=新PDO($save_path);

}

$this-dbh-setAttribute(PDO:ATTR_ERRMODE, PDO:ERRMODE_EXCEPTION);

//创建会话表的方法(使用异常处理)

尝试{

$this-dbh-query("从会话LIMIT 1 中选择1");

} catch (异常$e) {

$this-createTable($save_path, NULL, false);

}

}

接下来,我们演示如何将此类与session_set_save_handler() 结合起来将会话数据存储到数据库中。

包括__DIR__ 。 "/db.php";

ini_set("session.save_path", "sqlite:/tmp/sessions.db");

session_set_save_handler(新的DBHandler);

会话开始();

if (!isset($_SESSION["访问"])) {

$_SESSION["访问"]=0;

}

$_SESSION["访问"]++;

print "您已访问过此处".$_SESSION["visits"]."次。此代码块假定它与db.php 位于同一目录中。一旦session.save_path 设置为指定的PDO DSN,只需session_set_save_handler( new DBHandler);将PHP 与该程序关联起来。此后,使用会话的代码与使用PHP 默认处理程序的代码相同。

以上功能并不全面。建议前往http://php.net/查看详情。

文章到此结束,如果本次分享的高效会话与数据持久化解决方案详解和的问题解决了您的问题,那么我们由衷的感到高兴!

用户评论

陌上花

这篇应该讲的是怎么让聊天记录不会一关机就没了!

    有9位网友表示赞同!

古巷青灯

我一直在寻找这种技术,这样可以查看之前的对话内容。

    有17位网友表示赞同!

鹿叹

数据持久存储真是一门很神奇的知识!希望这篇文章能让我更了解它。

    有6位网友表示赞同!

﹎℡默默的爱

这方面的技术太需要啦!现在聊天软件很多都没这个功能,真的浪费时间

    有12位网友表示赞同!

秘密

我的聊天记录太多了,希望能用这种方法整理一下。

    有15位网友表示赞同!

半梦半醒i

文章内容会不会涉及到哪些平台常用数据存储的方式?

    有5位网友表示赞同!

凉凉凉”凉但是人心

真期待这篇文章能提供一些具体的实用方法,比如我可以自己实现数据持久存储吗?

    有8位网友表示赞同!

我就是这样一个人

学习新东西总是让人兴奋!希望这篇文章能够深入浅出地解释。

    有11位网友表示赞同!

若他只爱我。

我想知道数据持久存储对不同类型的应用有什么影响?

    有19位网友表示赞同!

执念,爱

这个技术听起来很有用,可以让我更好地利用聊天记录。

    有6位网友表示赞同!