Skip to content

Instantly share code, notes, and snippets.

@xuecan
Last active June 3, 2016 12:53
Show Gist options
  • Select an option

  • Save xuecan/2b1cd080ebedd2b6b6ec684e8b77c7d0 to your computer and use it in GitHub Desktop.

Select an option

Save xuecan/2b1cd080ebedd2b6b6ec684e8b77c7d0 to your computer and use it in GitHub Desktop.
<?php
/**
* @copyright Copyright (c) 2016, 北京联宇益通科技发展有限公司
*/
namespace Netpas\Common\SessionHandler;
use SessionHandlerInterface;
use Netpas\Exception\Exception;
/**
* 使用 $_COOKIE 和 setcookie() 实现的 Session 处理器。
*
* @author Xue Can <xuecan@netpas.co>
*/
class CookieSessionHandler implements SessionHandlerInterface
{
/** @var string 加密算法 */
protected $cipherMethod = '';
/** @var int 加密算法需要的 IV 长度 */
protected $ivLength = 0;
/** @var string 密码,为空则使用 Session ID 作为密码 */
protected $password = '';
/** @var string 保存数据的 Cookie 名称 */
protected $cookieName = '';
/**
* 构造方法。
*
* @param string $password 密码。
* @param string $cipherMethod 加密算法。默认为 `AES-256-CBC`。使用 `openssl_get_cipher_methods()`
* 了解可用的加密算法。
*/
public function __construct($password, $cipherMethod = 'AES-256-CBC')
{
$password = strval($password);
$cipherMethod = strval($cipherMethod);
if ('' === $password) {
$msg = 'Cipher password should not be empty';
throw new Exception($msg);
}
$this->password = strval($password);
$this->ivLength = openssl_cipher_iv_length($cipherMethod);
if (false === $this->ivLength) {
$msg = sprintf('Unknown cipher algorithm "%s"', $cipherMethod);
throw new Exception($msg);
}
$this->cipherMethod = $cipherMethod;
$cookieName = session_name();
if ('ID' === strtoupper(substr($cookieName, -2))) {
$cookieName = substr($cookieName, 0, strlen($cookieName) - 2);
}
$cookieName = $cookieName.'DATA';
$this->cookieName = $cookieName;
}
/**
* 读取 Session 数据。
*
* @param string $sessionId Session 标识。
*
* @return string Session 数据,如果无法获取必要的数据,则返回空字符串。
*/
public function read($sessionId)
{
$password = $this->password;
$iv = substr($sessionId, 0, $this->ivLength);
$raw = $_COOKIE[$this->cookieName] ?? '';
if (empty($raw)) {
return '';
}
$parts = explode('|', $raw);
if (2 != count($parts)) {
return '';
}
$encrypted = base64_decode($parts[0]);
$digest = $parts[1];
$cipherMethod = $this->cipherMethod;
$data = openssl_decrypt($encrypted, $cipherMethod, $password, 0, $iv);
if ($digest !== hash('sha256', $data)) {
return '';
}
return $data;
}
/**
* 写入 Session 数据。
*
* @param string $sessionId Session 标识。
* @param string $data 要写入的数据。
*
* @return bool 成功返回 `true`,失败返回 `false`。
*/
public function write($sessionId, $data)
{
$cookieName = $this->cookieName;
$digest = hash('sha256', $data);
$cipherMethod = $this->cipherMethod;
$password = $this->password;
$iv = substr($sessionId, 0, $this->ivLength);
$encrypted = openssl_encrypt($data, $cipherMethod, $password, 0, $iv);
$raw = base64_encode($encrypted).'|'.$digest;
$params = session_get_cookie_params();
$expire = time() + $params['lifetime'];
$path = $params['path'];
$domain = $params['domain'];
$secure = $params['secure'];
$httponly = $params['httponly'];
return setcookie($cookieName, $raw, $expire, $path, $domain, $secure, $httponly);
}
/**
* 销毁 Session 记录。
*
* @param string $sessionId Session 标识。
*
* @return bool 成功返回 `true`,失败返回 `false`。
*/
public function destroy($sessionId)
{
$cookieName = $this->cookieName;
$params = session_get_cookie_params();
$expire = 864000;
$path = $params['path'];
$domain = $params['domain'];
$secure = $params['secure'];
$httponly = $params['httponly'];
return setcookie($cookieName, '', $expire, $path, $domain, $secure, $httponly);
}
/**
* 初始化 Session 机制。
*
* @param string $savePath 接口需要的参数。
* @param string $sessionName 接口需要的参数。
*
* @return bool 总是返回 `true`。
*/
public function open($savePath, $sessionName)
{
return true;
}
/**
* 关闭 Session。
*
* @return bool 总是返回 `true`。
*/
public function close()
{
return true;
}
/**
* 清除过期的 Session 记录。
*
* @param int $maxlifetime 接口需要的参数。
*
* @return bool 总是返回 `true`。
*/
public function gc($maxlifetime)
{
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment