阿里云新用户优惠

第十一章 PHP会话控制

一、会话控制简述

  会话控制是一种面向连接的可靠通信方式,通常根据会话控制记录判断用户登录的行为。例如,当我们登录某个邮箱系统以后,可以在其中完成查看邮件、收信、发信等操作,这些操作有可能需要访问多个页面来完成。

  在同一个系统上,多个页面之间互相切换时,还能保持用户的登录状态,并且访问的都是用户自己的信息。这种能够在网站中跟踪一个用户,并且可以处理在同一个网站中同一个用户在多个页面共享数据的机制,都需要使用会话控制的思想完成。

1、为什么要使用会话控制

  我们在浏览网页时,访问每一个 Web 页面都需要使用到“HTTP 协议”,而 HTTP 协议是无状态协议,也就是说 HTTP 协议没有一个内建机制来维护两个事务之间的状态。当一个用户请求一个页面以后,再请求同一个网站上的另外一个页面时,HTTP 协议不能告诉我们这两个请求是来自同一个用户,会被当做独立的请求,而并不会将这两次访问联系在一起。

2、会话控制的方式

  由于 HTTP 是无状态的协议,所以不能维护两个事务之间的状态。PHP 系统为了防止这种情况的发生,提供了如下三种网页之间传递数据的方法。

  • 使用超链接或者 header() 函数,并在 URL 的 GET 请求中附加参数的形式,将数据从一个页面转向另一个 PHP 脚本中。也可以通过网页中的各种隐藏表单来储存使用者的资料,并将这些信息在提交表单时传递给服务器中的 PHP 脚本;
  • 使用 Cookie 将用户的状态信息存放在浏览器中,并通过浏览器来存取 Cookie 中的信息;
  • 相对于 Cookie 还可以使用 Session,将访问者的状态信息存放于服务器之中,让其他程序能透过服务器中的文件或数据库,来存取使用者的信息。

二、Cookie会话技术

  Cookie 是在 HTTP 协议下,服务器或脚本用来维护客户端上信息的一种方式。Cookie 是由 Web 服务器保存在用户浏览器(客户端)上的小文本文件,它可以包含有关用户的信息。无论何时用户链接到服务器,Web 站点都可以访问 Cookie 信息。

  有些 Cookie 是临时的,有些则是持续的。临时的 Cookie 只在浏览器上保存一段规定的时间,一旦超过规定的时间,该 Cookie 就会被系统清除。

1、Cookie 的主要用途

  服务器可以利用 Cookie 包含信息的任意性来筛选并维护这些信息,以判断 HTTP 传输中的状态。Cookie 最典型的应用是判定注册用户是否已经登录网站,同时用户可能也会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续,这些都是 Cookie 的功用。

  另一个重要应用场合就是商城类网站的“购物车”功能。用户可能会在一段时间内在同一家网站的不同页面中选择不同的商品,这些信息都会写入 Cookie,以便在最后付款时提取信息。

2、Cookie 的生命周期

  Cookie 可以保持登录信息到用户下次与服务器的会话,换句话说,下次访问同一网站时,用户会发现不必输入用户名和密码就已经登录了(当然,如果手动删除 Cookie 就需要重新登陆了)。而还有一些 Cookie 在用户退出会话的时候就被删除了,这样可以有效保护个人隐私。

  Cookie 在生成时就会被指定一个 Expire 值,这就是 Cookie 的生存周期,在这个周期内 Cookie 有效,超出这个周期 Cookie 就会被清除。有些页面将 Cookie 的生存周期设置为“0”或负值,这样在关闭浏览器时,就会马上清除 Cookie,不会记录用户信息,更加安全。

3、Cookie在浏览器中是如何存储的?

Cookie 是存储在客户端的一段数据,但是不同的浏览器存储 Cookie 的地方不同:

  • 一种是将 Cookie 数据保存在文件中, 谷歌浏览器C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\Cookies
  • 另一种是保存在浏览器内存中。

4、创建Cookie

  Cookie 的建立十分简单,只要用户的浏览器支持 Cookie 功能,就可以使用 PHP 中的 setcookie() 函数来设置一个 Cookie。

  Cookie 是 HTTP 响应头的一部分,而响应头必须在页面其他内容之前发送,它必须最先输出。setcookie() 函数必须位于<html>标签之前。

函数的语法格式如下:

  • setcookie(string $name [, string $value = "" [, int $expire = 0 [, string $path = "" [, string $domain = "" [, bool $secure = false [, bool $httponly = false ]]]]]])
    • $name:设置 Cookie 的名称;
    • $value:可选参数,用来设置 Cookie 的值。可以通过 $_COOKIE[’$name’] 的形式来获取 $value 的值;
    • $expire:可选参数,用来设置 Cookie 的过期时间,这个时间是 Unix 时间戳的形式。如果设置成零或者忽略该参数,Cookie 会在会话结束时过期(也就是关掉浏览器时);
    • $path:可选参数,用来设置 Cookie 有效的服务器路径。 设置成 ‘/’ 时,Cookie 对整个域名 $domain 有效。 如果设置成’/foo/’,则 Cookie 仅仅对 $domain 中 /foo/ 目录及其子目录有效(比如 /foo/bar/)。默认值为设置 Cookie 时的目录;
    • $domain:可选参数,用来设置 Cookie 的有效域名/子域名。设置成子域名(例如 ‘c.php.net’),会使 Cookie 对这个子域名和它的三级域名有效(例如 php.c.php.net)。 要让 Cookie 对整个域有效(包括它的全部子域名),只要设置成域名就可以了(例如 ‘php.net’);
    • $secure:可选参数,用来设置这个 Cookie 是否仅仅通过安全的 HTTPS 连接传给客户端。设置成 TRUE 时,只有安全连接存在时才会设置 Cookie;
    • $httponly:可选参数,设置成 TRUE 时,Cookie 仅可通过 HTTP 协议访问,也就是说 Cookie 无法通过类似 JavaScript 这样的脚本语言访问。设置该参数可以有效的减少受到 XSS 攻击的风险。
<?php
    //当第一次运行这个脚本文件时并不会有任何输出信息,因此设置完 Cookie 后需要刷新一下页面,这样在下次请求时 HTTP 头部才会携带上一次设置的 Cookie 信息,这时才能读取到 Cookie。
    setcookie('Website', 'PHP中文网');
    setcookie('Title', 'Cookie', time()+3600);  // 设置 Cookie 1 小时后过期
    setcookie('Url', 'http://php.net/', time()+3600, '/', 'php.net', false);
    echo '<pre>';
    print_r($_COOKIE);
?>

5、读取Cookie

使用超全局变量 $_COOKIE 获取 Cookie 的值。

<?php
    if(!isset($_COOKIE['time'])){                           //检测 Cookie 文件是否存在
        setcookie('time',date('Y-m-d H:i:s'));              //设置一个 Cookie 变量
        echo "第一次访问<br>";
    }else{
        echo "上次访问的时间为:".$_COOKIE['time'].'<br>';    //输出上次访问网站的时间
        setcookie('time',date('Y-m-d H:i:s'),time()+60);      //设置保存 Cookie 失效的时间的变量
    }
    echo "本次访问的时间为:".date('Y-m-d H:i:s');            //输出当前的访问时间
?>

6、删除Cookie

  当 Cookie 被创建后,如果没有设置它的失效时间,其 Cookie 文件会在关闭浏览器时被自动删除,如果要在关闭浏览器之前删除 Cookie 文件,同样需要使用 setcookie() 函数。

  删除 Cookie 和创建 Cookie 的方式类似,只需要使用 setcookie() 函数将 Cookie 的值(也就是第二个参数)设置为空,或者将 Cookie 的过期时间(也就是第三个参数)设置为小于系统的当前时间即可。

使用 setcookie() 函数将 Cookie 的值设置为空的方式来清除 Cookie

<?php
    //首次运行创建名为 url、name 的两个 Cookie;再次运行可以查看 Cookie 的值,并清除其中 url 的值;第三次运行可以查看清除后的结果
    echo '<pre>';
    if(!isset($_COOKIE['url']) && !isset($_COOKIE['name'])){
        setcookie('url','https://xiyoudaodao.gitee.io/');
        setcookie('name','西柚叨叨的博客');
        echo '首次运行,设置 url、name 两个 Cookie 的值';
    }else if(isset($_COOKIE['url'])){
        echo '查看 Cookie 的值,如下所示:<br>';
        print_r($_COOKIE);
        echo '清除 url 的值';
        //设置第二个参数为空的方式清除 Cookie
        setcookie('url','');
        //设置为小于系统的当前时间的方式清除 Cookie
        //setcookie('url','https://xiyoudaodao.gitee.io/', time()-1);
    }else{
        print_r($_COOKIE);
    }
?>

7、Cookie的优点与缺点

  • Cookie 的优点
    • 易于使用和实现
    • 占用内存少,不需要任何服务器资源
    • 持久性,设置 Cookie 时,它可以持续数天,数月甚至数年
    • 易于管理,用户可以轻松清除保存的 Cookie 信息
  • Cookie 的缺点
    • 隐私问题,第三方也可以访问这些 Cookie 信息
    • 不安全,明文形式存储,任何人都可以打开并篡改
    • 大小有限制,只能储存简单字符串信息,文本的大小一般为 3kb,每个站点只能容纳 30-50 个 Cookie 信息
    • 可以被用户禁用

三、 Session会话技术

  在 PHP 中,Session 是一种服务器端的机制,服务器使用一种散列表的结构(类似于 JSON)来保存信息。相比于保存在客户端的 Cookie,Session 将用户交互信息保存在了服务器端,使得同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只需要传回一个 ID 即可,这个 ID 是客户端第一次访问服务器的时候生成的,而且是唯一的。

  还有一点就是,因为 Cookie 存储在客户端,所以用户有权禁用 Cookie,而 Session 是存储在服务器端的,用户无法禁用。

1、Session 简介

  Session 在 Web 技术中占有非常重要的地位。由于网页是一种无状态的连接程序,无法记录用户的浏览状态,所以需要通过 Session 来记录用户的有关信息,以供用户再次以这个身份对 Web 服务器发起请求。

  Session 中文是“会话”的意思,与 Cookie 类似,都是用来储存使用者相关资料的,比如用户名、访问权限、登陆时间等。与 Cookie 最大不同之处在于 Cookie 是将资料存放于客户端电脑之中,而 Session 则是将数据存放于服务器系统之下。

  当开启一个 Session 时,PHP 将会创建一个随机的 Session ID(例如“t5is1r7ct740dn390kuv3mpcse”),每个用户的 Session ID 都是唯一的,而且 Session ID 与服务器上存储该用户 Session 数据的文本文件名称相同。

  • Session ID 会分别保存在客户端和服务器端两个位置。
    • 客户端,使用临时的 Cookie 保存在浏览器指定目录中,Cookie 名称默认为“PHPSESSID”;
    • 服务器端,以文本文件形式保存在指定的 Session 目录中。

默认情况下,这个 Session ID 将作为一个 Cookie 发送给 Web 浏览器,接下来 PHP 页面将使用这个 Cookie 来访问 Session 的信息。

  • 与 Cookie 相比,Session 拥有以下的优势:
    • 通常情况下 Session 更加安全,因为 Session 中的数据不会在客户端和服务器端来回重复传递;
    • Session 能够存储比 Cookie 更多的信息;
    • 在用户禁用 Cookie 的情况下,使用一些方法任然能保持 Session 正常工作。

2、Session 的存储方式

Session 默认会以文本的形式存储在服务器的临时目录中,文件名以“sess_”作为前缀,后面加上“Session ID”,例如“sess_t5is1r7ct740dn390kuv3mpcse”。

我们可以在 php.ini 中设置 Session 的相关配置,下面是一些常用的配置信息:

参数 描述
session.save_handler = files session 的存储方式,默认是文件,还可以是 redis 或者是 memcache
session.save_path = “d:/wamp/tmp” session 文件的存储目录
session.use_cookies = 1 是否使用 cookie 存储 session_id
session.name = PHPSESSID 客户端存储 session_id 的会话名
session.auto_start = 0 是否自动开启 session
session.cookie_lifetime = 0 设置客户端中存储的 session_id 的过期时间,以秒为单位
session.use_only_cookies=0 是否只使用 cookie 来处理 session_id
session.gc_maxlifetime = 1440 设置 session 文件的过期时间

3、Session 的生命周期

Session 在以下情况会被删除,也就是失效:

  • Session 超时,超时指的是连续一定时间服务器没有收到该 Session 所对应客户端的请求,并且这个时间超过了服务器设置的 Session 超时的最大时间;
  • 程序调用方法主动销毁 Session;
  • 服务器关闭或服务停止。

4、设置和获取 Session

使用Session前需要在页面开头用session_start()方法开启Session功能。session_start()函数必须位于<html>标签之前。

$_SESSION 为一个关联数组,和普通关联数组键值对的含义相同。设置 Session 的语法格式如下: $_SESSION[名称] = 值;

<?php
    session_start();
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>存储和读取Session</title>
</head>
<body>
<?php
    $_SESSION["views"] = isset($_SESSION["views"]) ? ($_SESSION["views"]+1): 1;
    echo "浏览量:{$_SESSION["views"]}";
?>
</body>
</html>

5、删除 Session

① 使用 unset() 函数删除单个 Session 元素
<?php
    session_start();
    echo '<pre>';
    $_SESSION['name']  = "西柚叨叨";
    $_SESSION['title']  = "西柚叨叨的博客";
    echo '定义一个 Session,如下所示:<br>';
    print_r($_SESSION);
    echo '删除 Session 中名为 name 的元素:<br>';
    unset($_SESSION['name']);
    print_r($_SESSION);
?>
② 删除多个 Session 元素

如果想要一次性删除多个 Session 元素,即一次注销所有的会话变量,可以通过将一个空的数组赋值给 $_SESSION 来或者使用session_unset()实现,示例代码如下:

<?php
    session_start();
    echo '<pre>';
    $_SESSION['name']  = "西柚叨叨";
    $_SESSION['title']  = "西柚叨叨的博客";
    echo '定义一个 Session,如下所示:<br>';
    print_r($_SESSION);
    echo '删除 Session 中的多个元素:<br>';
    $_SESSION = [];
    // echo '使用 session_unset() 函数释放 Session 中的所有元素:<br>';
    // session_unset();
    print_r($_SESSION);
?>

session_unset()只能删除$_SESSION数组中的所有元素,不能删除对应的Session ID,也不能删除保存Session ID的文件

6、销毁 Session

如果整个 Session 会话结束,可以使用 session_destroy() 函数销毁当前会话的全部数据。
函数的语法格式如下: session_destroy()

<?php
    session_start();
    echo '<p>这个用户的Session编号为'.session_id().'</p>';
    $_SESSION['name'] = '小红';
    session_destroy();
    echo '<p>这个用户的Session编号为'.session_id().'</p>';
    echo $_SESSION['name'];
?>

session_destroy()可以删除对应的Session ID,也可以删除保存Session ID的文件,但不会删除内存中的$_SESSION数组中的元素。因此,如果实现用户安全注销功能,彻底删除Session,可以将session_unset()与session_destroy()结合使用,并且清除Cookie

7、Session和Cookie的区别和联系

  • Session与Cookie的主要区别如下:
    • 存放位置。
    • 存放形式。
    • 路径。
    • 用途。
    • 安全性。
    • 大小及数量限制。

Session与Cookie的联系:
  session需要借助cookie才能正常工作,如果客户端完全禁止cookie,session将失效,因为session是由应用服务器维持的一个服务端的存储空间,用户在连接服务器时,会由服务器生成唯一的sesssionid,用该sessionid为标识来存取服务端的session空间。而sessionid存储在cookie中,用户提交页面时会将这个sessionid提交到服务端,来存取session数据.这一过程是不用开发人员干预的,所以一旦客户端禁用cookie,那么session也会失效。

四、综合实例

1、使用Cookie实现自动登陆

<?php
    /**
     * 首页
     */
    function index(){
        $logout = isset($_POST['logout'])?$_POST['logout']:'';
        $user   = isset($_COOKIE['user'])?$_COOKIE['user']:'';
        $rem    = isset($_COOKIE['remember'])?$_COOKIE['remember']:'';
        if($logout == 'true'){  //判断是否执行退出登陆
            logout();
        }else if($user == ''){  //如果Cookie中没有用户信息则执行登陆操作
            login();
        }else{  //显示首页
            //首页的html代码
            $str = <<<html
                <!DOCTYPE html>
                <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <title>Cookie测试</title>
                </head>
                <body>
                    <form action="" method="post">
                        <p><input type="hidden" value="true" name="logout" /></p>
                        <p><input type="submit" value="退出登陆" /></p>
                    </form>
                </body>
                </html>
html;
            echo $str;
        }
    }
    /**
     * 登陆
     */
    function login(){
        //获取提交的用户信息
        $user   = isset($_POST['user'])?trim($_POST['user']):'';
        $pwd    = isset($_POST['pwd'])?trim($_POST['pwd']):'';
        $rem    = isset($_POST['remember'])?$_POST['remember']:'';
        if($user == ''){    //如果用户名为空,则显示登陆页面
            // 登陆页面的html代码
            $info = <<<html
                <!DOCTYPE html>
                <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <title>Cookie测试</title>
                </head>
                <body>
                    <form action="" method="post">
                        <p>用户名:<input type="text" name="user" /></p>
                        <p>密&emsp;码:<input type="password" name="pwd" /></p>
                        <p><input type="checkbox" name="remember" value='true'/>自动登陆</p>
                        <p><input type="submit" value="登 陆" />&emsp;&emsp;<input type="reset" value="重 置" /></p>
                    </form>
                </body>
                </html>
html;
            echo $info;
        }else{
            if(!empty($user) && !empty($pwd)){  // 登陆成功,并记录Cookie信息
                if($rem != ''){
                    setcookie('user',$user,time()+3600*24*7);
                    setcookie('remember',$rem,time()+3600*24*7);
                }else{
                    setcookie('user',$user);
                }
                echo '<script>alert(\'登陆成功\');location.replace(location.href);</script>';
            }else{  //登陆失败时,刷新页面
                echo '<script>alert(\'用户名或密码不能为空\');location.replace(location.href);</script>';
            }
        }
    }
    /**
     * 退出登陆
     */
    function logout(){
        // 清除 Cookie 信息,并刷新页面
        isset($_COOKIE['user']) ? setcookie('user','',time()-1) : '';
        isset($_COOKIE['remember']) ? setcookie('remember','',time()-1) : '';
        echo '<script>alert(\'退出成功\');location.replace(location.href);</script>';
    }
   
    index(); //执行 index 函数
?>

2、使用Session登陆

<?php
session_start();
/**
     * 首页
     */
    function index(){
        $logout = isset($_POST['logout'])?$_POST['logout']:'';
        $user   = isset($_SESSION['user'])?$_SESSION['user']:'';
        if($logout == 'true'){  //判断是否执行退出登陆
            logout();
        }else if($user == ''){  //如果Session中没有用户信息则执行登陆操作
            login();
        }else{  //显示首页
            //首页的html代码
            $str = <<<html
                <!DOCTYPE html>
                <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <title>Session测试</title>
                </head>
                <body>
                    <form action="" method="post">
                        <p><input type="hidden" value="true" name="logout" /></p>
                        <p>用户名:{$user}</p>
                        <p><input type="submit" value="退出登陆" /></p>
                    </form>
                </body>
                </html>
html;
            echo $str;
        }
    }
    /**
     * 登陆
     */
    function login(){
        //获取提交的用户信息
        $user   = isset($_POST['user'])?trim($_POST['user']):'';
        $pwd    = isset($_POST['pwd'])?trim($_POST['pwd']):'';
        if($user == ''){    //如果用户名为空,则显示登陆页面
            // 登陆页面的html代码
            $info = <<<html
                <!DOCTYPE html>
                <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <title>Session测试</title>
                </head>
                <body>
                    <form action="" method="post">
                        <p>用户名:<input type="text" name="user" /></p>
                        <p>密&emsp;码:<input type="password" name="pwd" /></p>
                        <p><input type="submit" value="登 陆" />&emsp;&emsp;<input type="reset" value="重 置" /></p>
                    </form>
                </body>
                </html>
html;
            echo $info;
        }else{
            if(!empty($user) && !empty($pwd)){
                $_SESSION['user'] = $_POST['user'];
                echo '<script>alert(\'登陆成功\');location.replace(location.href);</script>';
            }else{  //登陆失败时,刷新页面
                echo '<script>alert(\'用户名或密码不能为空\');location.replace(location.href);</script>';
            }
        }
    }
    /**
     * 退出登陆
     */
    function logout(){
        // 清除 session 信息,并刷新页面
        session_unset();
        session_destroy();
        echo '<script>alert(\'退出成功\');location.replace(location.href);</script>';
    }
   
    index(); //执行 index 函数

关注微信公众号,与我交流吧~

分享