通过session和cookie记录登录状态
基本思路
最近开发了一个http://day.wubin.work/,专门用来记录日报,生成月报之类的的一个小应用。里面用到了通过session和cookie记录登录的操作,特此记录一下。(像我这个博客之前使用了session_id())记录登录状态,在chrome某次更新后发现失效了,最近一直修改,发现cookie记录了,但是服务器的session存储没有了~~)
这里用到的基本思路就是:
- 首先登录验证通过后,将登录信息记录到session中
- 将登录信息存入数组中(或者字符串)中,然后进行加密(PHP加密参考我的另一篇文章)
- 将生成的加密字符串存入cookie中(cookie的键可以与session名相同,或者不同),设置过期时间
- 每次登录的时候先判断,如果session不存在了,就判断cookie中是否有值,如果有,那么取出值,解密,在用户表中进行一次查询,如果信息匹配,那么给session重新赋值。
- 大部分可以在这个思路上进行变化,根据实际应用情况进行修改
参考的一个案例
<?php
// cookie存储的路径
define('COOKIE_PATH', '/');
// cookie过期时间-天
define('COOKIE_EXPIRE', 7);
// 生效的域名
define('COOKIE_DOMAIN', 'day.wubin.work');
// 这里也可以使用session_name() 或者自己指定
define('COOKIE_NAME', '****');
$passwordMD5 = md5($password);
$sql = "
SELECT `user_id`,
`name`,
`password`,
`nikename`
FROM `day_user`
WHERE name = ? AND password = ?
";
$res_db = DB::query($sql, ...);
if (count($res_db) <= 0) {
// 登陆密码有误,退出
exit();
} else {
// 如果信息匹配成功 且点击了记住我,那么同时将信息存在cookie中
if ($remberme == 1) {
$cookie = CustomCookie::getInstance();
// 将用户名和加密后的密码统一进行加密
$token = md5($username . $passwordMD5);
$cookie->set(
COOKIE_NAME,
// 这里简单的将信息拼接成字符串
// 这里可以赋值给数组,然后使用函数加密 读取的时候解密即可
"{$res_db[0]['user_id']}|{$token}",
[
// 过期时间7天
'expire' => time() + COOKIE_EXPIRE * 24 * 60 * 60,
'path' => COOKIE_PATH,
'domain' => COOKIE_DOMAIN,
'httponly' => true
]
);
}
// 如果验证成功 那么将值保存在session中
$_SESSION['user_id'] = $res_db[0]['user_id'];
$_SESSION['user_name'] = $res_db[0]['name'];
$_SESSION['nikename'] = $res_db[0]['nikename'];
header('Location: 主页.html');
}
<?php
/*
原理是将登陆信息存在cookie中 如果session中没信息 就看cookie有没有
cookie里面有信息 那么就取出来 执行一次查询 匹配 那么给session重新赋值查询的结果
*/
try {
// 当session中不存在记录的时候
if (!isset($_SESSION['user_id']) ||
!isset($_SESSION['user_name']) ||
!isset($_SESSION['nikename'])
) {
$cookie = CustomCookie::getInstance();
$cookieValue = $cookie->get(COOKIE_NAME);
if ($cookieValue) {
// 如果cookie存在,那么执行一次查询
$tokenArr = explode("|", $cookieValue);
$userid = $tokenArr[0];
$token = $tokenArr[1];
$sql = "
SELECT *
FROM day_user
WHERE user_id = ?
";
$resDB = DB::query($sql ...);
// 如果查询成功 代表用户存在
if ($resDB && count($resDB) > 0) {
// 将得到的信息进行拼接,并使用md5加密,比较字符串是否相同,相同代表信息匹配
$tokenInDb = md5($resDB[0]['name'] . $resDB[0]['password']);
// 如果验证相等 那么给session赋值
if ($token == $tokenInDb) {
$_SESSION['user_name'] = $resDB[0]['name'];
$_SESSION['user_id'] = $resDB[0]['user_id'];
$_SESSION['nikename'] = $resDB[0]['nikename'];
} else {
throw new Exception("抱歉,您的登录信息与验证不符,请重新登录");
}
} else {
throw new Exception("抱歉,该用户不存在!");
}
} else {
throw new Exception("您还没登录,请先登录!");
}
}
} catch(Exception $e) {
jsAlertPlus($e->getMessage(), 'jumpto', 'login.php');
exit();
}
3、退出登录时候,同时注销session和cookie,注意,注销cookie的参数要与存储时候的参数相同!
unset($_SESSION['user_id']);
unset($_SESSION['user_name']);
unset($_SESSION['nikename']);
session_destroy();
// 删除session的同时也删除cookie
$cookie = CustomCookie::getInstance();
// 删除的时候要传入与设置时一致的参数
$cookie->delete(COOKIE_NAME, [
'path' => COOKIE_PATH,
'domain' => COOKIE_DOMAIN,
'httponly' => true
]);
4、【备忘】记录使用加密函数记录cookie
$dataArr['USER_NAME'] = ***;
$dataArr['USER_ID'] = ***;
$dataArr['USER_ISLOGIN'] = ***;
$dataArr['USER_NIKENAME'] = ****;
// 将要加密的数据转化为json对象并且加密
$data = json_encode($dataArr, JSON_FORCE_OBJECT);
// 密钥
$key = '123456';
// 加密数据 'AES-128-ECB' 可以通过openssl_get_cipher_methods()获取
$encrypt = openssl_encrypt($data, 'AES-128-ECB', $key, 0);
return $encrypt;
文章中用到的cookie操作类
<?php
/*
cookie设置 读取 更新 删除
封装cookie操作类
*/
class CustomCookie
{
static private $_instance = null;
private $expire = 0;
private $path = '';
private $domain = '';
private $secure = false;
private $httponly = false;
// 构造函数接收cookie参数完成初始化工作
private function __construct(array $options = []) {
$this->setOptions($options);
}
// 设置相关选项
private function setOptions(array $options = []) {
// 检测数组中是否有过期时间项,如果有就对类中变量赋值
if (isset($options['expire'])) {
// 转换为整型
$this->expire = (int)$options['expire'];
}
if (isset($options['path'])) {
$this->path = $options['path'];
}
if (isset($options['domain'])) {
$this->domain = $options['domain'];
}
if (isset($options['domain'])) {
$this->domain = $options['domain'];
}
if (isset($options['secure'])) {
// 设置为布尔类型
$this->secure = (bool)$options['secure'];
}
if (isset($options['httponly'])) {
$this->httponly = (bool)$options['httponly'];
}
// 如果想使用链式操作就返回this
return $this;
}
// 单例模式 $options = cookie相关选项
// 单例模式就可以实现多次实例化一个类只对应一个内存资源
public static function getInstance(array $options = []) {
if (is_null(self::$_instance)) {
// 获取类名
$class = __CLASS__;
self::$_instance = new $class($options);
}
// 返回对象实例
return self::$_instance;
}
// 设置cookie
/*
$name cookie名
$value cookie值
$options cookie选项
*/
public function set($name, $value, array $options = []) {
// 如果传了数组并且数组长度>0
if (is_array($options) && count($options) > 0) {
// 就设置相关选项
$this->setOptions($options);
}
// 如果值是数组或者对象就转化成json字符串格式数据,使用的时候就需要再转化成数组等
if (is_array($value) || is_object($value)) {
// 传第二个参数全部转化为对象,所以转换后左边一定是个{号
$value = json_encode($value, JSON_FORCE_OBJECT);
}
// 如果没有传就使用默认值
setcookie($name,
$value,
$this->expire,
$this->path,
$this->domain,
$this->secure,
$this->httponly
);
}
/*
* 得到执行cookie
@param string $name cookie名称
@return mixed 返回null或者对象或者标量
-----------------------------------------
在get/set/delete中的参数$name限定string类型,在php7才有效,php5只支持objece和array,这里先去掉
public function get(string $name) {}
*/
public function get($name) {
// 先判断是否存在这样cookie
if (isset($_COOKIE[$name])) {
// 检测第一个字符是不是{左花括号,是的话证明原来的值是一个数组,需要再将对象转化为数组,否则就直接返回字符串等
return substr($_COOKIE[$name], 0, 1) == '{' ? json_decode($_COOKIE[$name]) : $_COOKIE[$name];
} else {
return null;
}
}
/*
注意设置的时候有选项,删除的时候选项也要原封不动的传递保持一致
删除指定cookie
$name cookie名称
$options cookie相关选项
*/
public function delete($name, array $options = []) {
if (is_array($options) && count($options) > 0) {
$this->setOptions($options);
}
// 先检测$_COOKIE是否存在要删除的
if (isset($_COOKIE[$name])) {
// 删除让过期时间-1即可,值就设置为空,其余参数必须与设置时保持一致
setcookie($name,
'',
time() - 1,
$this->path,
$this->domain,
$this->secure,
$this->httponly
);
unset($_COOKIE[$name]);
}
}
/* 删除所有cookie,同样的也要保证过期时间等要相同 */
public function deleteAll(array $options = []) {
if (is_array($options) && count($options) > 0) {
$this->setOptions($options);
}
// 如果$_COOKIE不为空
if (!empty($_COOKIE)) {
// 循环给每个cookie设置过期时间
foreach ($_COOKIE as $name => $value) {
setcookie(
$name,
'',
time() - 1,
$this->path,
$this->domain,
$this->secure,
$this->httponly
);
}
}
}
}
// $cookie = CustomCookie::getInstance();
// $cookie->set('aa', 111);
// $cookie->set('bb', 22);
// $cookie->set('ccc', 333, [
// 'expire' => time() + 3600
// ]);
// $cookie->set('userInfo', [
// 'name' => 'king',
// 'age' => 23
// ]);
// var_dump($cookie->get('userInfo'));
// var_dump($cookie->get('aa'));
// $cookie->delete('aa');
// $cookie->delete('userInfo');
// $cookie->deleteAll();
?>