thinkphp5.1 多个域跳转问题session共享解决方案

系列文章目录

最近遇到一个多个域名跳转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不能被清除,浏览器不能关闭!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值