系列文章目录
最近遇到一个多个域名跳转session的问题,这里写个Demo记录下思路文章目录
准备条件
1.框架 thinkphp5.1
2.服务器apache
3.MySQL数据库
一.问题描述
假设我有两个不同域名的站点 a.com ,b.com ,这两个站点之间都有登录,作为一个用户,我希望在a.com 登录之后我就可以不用到b.com登录,而是可以直接进入b.com的首页,那么我该如何实现呢?
二.实现过程
1. 编写两个站点的登录控制程序(要求,未登录不能访问首页及相关接口,可以使用中间件实现)
在这里插入图片描述
这个页面是a.com b.com 的统一登录入口
2.在这个统一入口输入账号密码,例如 admin 123456,后端程序验证成功后,获取从cookie中得到的sessionID 并跳转到一个提示页面,就叫info.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>正在跳转中...</title>
</head>
<body>
正在跳转....
<iframe width="0" height="0" src="http://a.com/index/login/set_cookie?sid={$sid}"></iframe>
<iframe width="0" height="0" src="http://b.com/index/login/set_cookie?sid={$sid}"></iframe>
</body>
</html>
<script>
setTimeout(function () {
window.location.href='http://b.com/index/index/index';
},1000);
</script>
其中a.com b.com 的set_cookie方法如下:
public function set_cookie()
{
$sid = input('sid');
session_id($sid);
session_start();
}
3.重写thinkphp的session保存机制
我们知道,php 默认的session都是保存到文件里面的,当有多个站点多个服务器的时候,每一个服务器都会产生一个文件来保存用户登录的session信息,每一个文件里面无法建立联系,因此多个服务器的用户session信息不能共享。我们可以php 的sessionID 保存到redis 或者mysql数据库当中,我这里保存到mySQL数据库中。
php为session保存机制预留了接口,我们只要实现它的标准,就可以把sessionID 保存到我们想保存的位置
<?php
/**
* <b>SessionHandlerInterface</b> is an interface which defines
* a prototype for creating a custom session handler.
* In order to pass a custom session handler to
* session_set_save_handler() using its OOP invocation,
* the class must implement this interface.
* @link http://php.net/manual/en/class.sessionhandlerinterface.php
* @since 5.4.0
*/
interface SessionHandlerInterface {
/**
* Close the session
* @link http://php.net/manual/en/sessionhandlerinterface.close.php
* @return bool <p>
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function close();
/**
* Destroy a session
* @link http://php.net/manual/en/sessionhandlerinterface.destroy.php
* @param string $session_id The session ID being destroyed.
* @return bool <p>
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function destroy($session_id);
/**
* Cleanup old sessions
* @link http://php.net/manual/en/sessionhandlerinterface.gc.php
* @param int $maxlifetime <p>
* Sessions that have not updated for
* the last maxlifetime seconds will be removed.
* </p>
* @return bool <p>
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function gc($maxlifetime);
/**
* Initialize session
* @link http://php.net/manual/en/sessionhandlerinterface.open.php
* @param string $save_path The path where to store/retrieve the session.
* @param string $name The session name.
* @return bool <p>
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function open($save_path, $name);
/**
* Read session data
* @link http://php.net/manual/en/sessionhandlerinterface.read.php
* @param string $session_id The session id to read data for.
* @return string <p>
* Returns an encoded string of the read data.
* If nothing was read, it must return an empty string.
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function read($session_id);
/**
* Write session data
* @link http://php.net/manual/en/sessionhandlerinterface.write.php
* @param string $session_id The session id.
* @param string $session_data <p>
* The encoded session data. This data is the
* result of the PHP internally encoding
* the $_SESSION superglobal to a serialized
* string and passing it as this parameter.
* Please note sessions use an alternative serialization method.
* </p>
* @return bool <p>
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function write($session_id, $session_data);
}
/**
* <b>SessionUpdateTimestampHandlerInterface</b> is an interface which
* defines a prototype for updating the life time of an existing session.
* In order to use the lazy_write option must be enabled and a custom session
* handler must implement this interface.
* @since 7.0.0
*/
interface SessionUpdateTimestampHandlerInterface {
/**
* Validate session id
* @param string $session_id The session id
* @return bool <p>
* Note this value is returned internally to PHP for processing.
* </p>
*/
public function validateId($session_id);
/**
* Update timestamp of a session
* @param string $session_id The session id
* @param string $session_data <p>
* The encoded session data. This data is the
* result of the PHP internally encoding
* the $_SESSION superglobal to a serialized
* string and passing it as this parameter.
* Please note sessions use an alternative serialization method.
* </p>
* @return bool
*/
public function updateTimestamp($session_id, $session_data);
}
/**
* <b>SessionHandler</b> a special class that can
* be used to expose the current internal PHP session
* save handler by inheritance. There are six methods
* which wrap the six internal session save handler
* callbacks (open, close, read, write, destroy and gc).
* By default, this class will wrap whatever internal
* save handler is set as as defined by the
* session.save_handler configuration directive which is usually
* files by default. Other internal session save handlers are provided by
* PHP extensions such as SQLite (as sqlite),
* Memcache (as memcache), and Memcached (as memcached).
* @link http://php.net/manual/en/class.reflectionzendextension.php
* @since 5.4.0
*/
class SessionHandler implements SessionHandlerInterface, SessionUpdateTimestampHandlerInterface {
/**
* Close the session
* @link http://php.net/manual/en/sessionhandler.close.php
* @return bool <p>
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function close() { }
/**
* Return a new session ID
* @link http://php.net/manual/en/sessionhandler.create-sid.php
* @return string <p>A session ID valid for the default session handler.</p>
* @since 5.5.1
*/
public function create_sid() {}
/**
* Destroy a session
* @link http://php.net/manual/en/sessionhandler.destroy.php
* @param string $session_id The session ID being destroyed.
* @return bool <p>
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function destroy($session_id) { }
/**
* Cleanup old sessions
* @link http://php.net/manual/en/sessionhandler.gc.php
* @param int $maxlifetime <p>
* Sessions that have not updated for
* the last maxlifetime seconds will be removed.
* </p>
* @return bool <p>
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function gc($maxlifetime) { }
/**
* Initialize session
* @link http://php.net/manual/en/sessionhandler.open.php
* @param string $save_path The path where to store/retrieve the session.
* @param string $session_name The session name.
* @return bool <p>
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function open($save_path, $session_name) { }
/**
* Read session data
* @link http://php.net/manual/en/sessionhandler.read.php
* @param string $session_id The session id to read data for.
* @return string <p>
* Returns an encoded string of the read data.
* If nothing was read, it must return an empty string.
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function read($session_id) { }
/**
* Write session data
* @link http://php.net/manual/en/sessionhandler.write.php
* @param string $session_id The session id.
* @param string $session_data <p>
* The encoded session data. This data is the
* result of the PHP internally encoding
* the $_SESSION superglobal to a serialized
* string and passing it as this parameter.
* Please note sessions use an alternative serialization method.
* </p>
* @return bool <p>
* The return value (usually TRUE on success, FALSE on failure).
* Note this value is returned internally to PHP for processing.
* </p>
* @since 5.4.0
*/
public function write($session_id, $session_data) { }
/**
* Validate session id
* @param string $session_id The session id
* @return bool <p>
* Note this value is returned internally to PHP for processing.
* </p>
*/
public function validateId($session_id) { }
/**
* Update timestamp of a session
* @param string $session_id The session id
* @param string $session_data <p>
* The encoded session data. This data is the
* result of the PHP internally encoding
* the $_SESSION superglobal to a serialized
* string and passing it as this parameter.
* Please note sessions use an alternative serialization method.
* </p>
* @return bool
*/
public function updateTimestamp($session_id, $session_data) { }
}
这个就是定义sessionID处理机制的接口,实现它就可以了
我的实现如下:
<?php
/**
* Created by PhpStorm.
* User: php
* Date: 2021/5/14
* Time: 7:38
*/
namespace think\session\driver;
use SessionHandlerInterface; //PHP实现预留session接口
use think\Db;
use think\Exception;
use think\facade\Config;
class Mysql implements SessionHandlerInterface
{
protected $handler = null;
protected $config = [
'hostname' => '127.0.0.1',// 服务器地址
'database' => 'test',// 数据库名
'username' => 'root',// 用户名
'password' => 'root',// 密码
'hostport' => '3306',// 端口
'charset' => 'utf8mb4', // 数据库编码默认采用utf8
'expire' => 3600, // session有效期
'session_name' => '', // sessionkey前缀
'session_table' => 'think_session', // session存储的数据表名称
];
protected $table_name = null;
public function __construct($config = [])
{
//获取数据库配置,将数据库配置更新
$this->config = array_merge($this->config, Config::get('database.'), $config);
$this->table_name = empty($this->config['session_table']) ? $this->config['prefix'] . '_session' : $this->config['session_table'];
}
/**
* 打开Session
* @access public
* @param string $savePath
* @param mixed $sessName
*/
public function open($savePath, $sessName)
{
if (empty($this->config['hostname'])) throw new Exception('database config error');
$this->handler = Db::connect($this->config);
return true;
}
/**
* 关闭Session
* @access public
*/
public function close()
{
$this->gc(ini_get('session.gc_maxlifetime'));
$this->handler = null;
return true;
}
/**
* 读取Session
* @access public
* @param string $sessID
*/
public function read($sessID)
{
return (string)Db::table($this->table_name)->where([['session_id', '=', $this->config['session_name'] . $sessID], ['session_expire', '>=', time()]])->value('session_data');
}
/**
* 写入Session
* @access public
* @param string $sessID
* @param string $sessData
* @return bool
*/
public function write($sessID, $sessData)
{
//获取通过session传入的隐藏参数,用于其他操作
$unserialize_session_data = unserialize(explode('|', $sessData)[1]);
//构建存入数据库的数据
$params = [
'session_id' => $this->config['session_name'] . $sessID,
'session_expire' => $this->config['expire'] + time(),
'session_data' => $sessData,
'create_time' => date('Y-m-d H:i:s',time()),
'ip' => get_ip(),
];
$sql = "REPLACE INTO {$this->table_name} (session_id,session_expire,session_data,create_time,ip) VALUES (:session_id, :session_expire, :session_data,:create_time,:ip)";
$result = $this->handler->execute($sql, $params);
return $result ? true : false;
}
/**
* 删除Session
* @access public
* @param string $sessID
* @return bool
*/
public function destroy($sessID)
{
$result = $this->handler->execute("DELETE FROM {$this->table_name} WHERE session_id = :session_id", ['session_id' => $this->config['session_name'] . $sessID]);
return $result ? true : false;
}
/**
* Session 垃圾回收
* @access public
* @param string $sessMaxLifeTime
* @return true
*/
public function gc($sessMaxLifeTime)
{
$result = $this->handler->execute("DELETE FROM {$this->table_name} WHERE session_expire < :session_expire", ['session_expire' => time()]);
return $result ? true : false;
}
}
我这里是php 默认的sessionID 保存到本地mysql test数据库下,保存的表名think_session,表结构如下:
CREATE TABLE `think_session` (
`session_id` varchar(255) CHARACTER SET utf8 NOT NULL,
`session_expire` int(11) NOT NULL DEFAULT '0' COMMENT 'SESSION的过期时间',
`session_data` blob COMMENT 'SESSION的数据内容',
`create_time` datetime NOT NULL,
`ip` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '',
UNIQUE KEY `session_id` (`session_id`),
KEY `NewIndex1` (`session_expire`),
KEY `Uname` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
前面三大步做好了,就可以了,看哈效果
两个站点不同域之间的cookie都保存下来了,两个域之间的sessionID是一样的
这个样子,两个域之间就可以任性的自由免登录跳转,前提条件cookie不能被清除,浏览器不能关闭!