通过session和cookie记录登录状态

5405次阅读 159人点赞 作者: WuBin 发布时间: 2022-07-07 11:40:20
扫码到手机查看

基本思路

最近开发了一个http://day.wubin.work/,专门用来记录日报,生成月报之类的的一个小应用。里面用到了通过session和cookie记录登录的操作,特此记录一下。(像我这个博客之前使用了session_id())记录登录状态,在chrome某次更新后发现失效了,最近一直修改,发现cookie记录了,但是服务器的session存储没有了~~)

这里用到的基本思路就是:

  1. 首先登录验证通过后,将登录信息记录到session中
  2. 将登录信息存入数组中(或者字符串)中,然后进行加密(PHP加密参考我的另一篇文章
  3. 将生成的加密字符串存入cookie中(cookie的键可以与session名相同,或者不同),设置过期时间
  4. 每次登录的时候先判断,如果session不存在了,就判断cookie中是否有值,如果有,那么取出值,解密,在用户表中进行一次查询,如果信息匹配,那么给session重新赋值。
  5. 大部分可以在这个思路上进行变化,根据实际应用情况进行修改

参考的一个案例

1、当登录成功的时候,向session和cookie中存入信息
<?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');
}
2、验证登录,当session中不存在登录信息的时候,从cookie中查找
<?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();
 ?>

相关资料

点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:session、cookie记录登录状态
推荐阅读
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    9352次阅读 603人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • Vue

    盘点Vue2和Vue3的10种组件通信方式

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。

    4072次阅读 302人点赞 发布时间: 2022-08-19 09:40:16 立即查看
  • JS

    几个高级前端常用的API

    推荐4个前端开发中常用的高端API,分别是MutationObserver、IntersectionObserver、getComputedstyle、getBoundingClientRect、requ...

    14279次阅读 934人点赞 发布时间: 2021-11-11 09:39:54 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    13219次阅读 462人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    11801次阅读 943人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    15974次阅读 1273人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    15018次阅读 1223人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    29429次阅读 2309人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    25377次阅读 1954人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录