目录
一.Cookie
1.原理
2.代码实现
(1)admin-c.php
(2)Index-c.php
(3)logout-c.php
3.交作业
二.Session
1.原理
2.代码实现
(1)admin-s.php
(2)index-s.php
(3)logout-s.php
3.交作业
三.token
1.原理
3.代码实现
(1)token.php
(2)Token_check
3.作业
编辑
在前一天的基础上实现后台登录cookie、session和token的身份验证。表单是让deepseek生成的简单页面。
B站免费课程链接:
www.bilibili.com/video/BV12om2YTEgW/?spm_id_from=333.788.videopod.episodes&vd_source=7d8464f1ea919ffdc1761c211c6d1c4e&p=24
一.Cookie
1.原理
想象浏览器是一个每天都要去“网站大厦”办事的访客,服务器就是大厦前台。第一次你来的时候,报上账号密码,前台核对无误后,不会把你的身份证复印件贴在你脸上,而是递给你一张写着随机编号的寄存牌——这张寄存牌就是 Cookie。浏览器会乖乖把它收进口袋。以后你每次再去这栋大厦,还没等你开口,浏览器就自动把寄存牌掏出来递给前台:“喏,这是我的牌。”前台拿着编号去后台柜子里一查,就知道你是几号会员、有没有过期、是不是已经退房。牌子上没有你的密码,只有一串谁也猜不到的编号;就算你偷偷把编号改了,后台查不到对应记录,照样不认你。退出登录时,前台要么把这张牌作废,要么让浏览器直接把牌扔掉。
所以,Cookie 的工作机制就像一张“会自动出示的临时通行证”:服务器发牌,浏览器存牌,每次访问同一网站时自动亮牌,服务器凭牌去后台核对身份,再决定放不放行。
Cookie是保存在浏览器,只要访问的cookie正确就能通过校验。常常会有钓鱼来做cookie劫持,点击不知名链接就自动访问敏感站点,然后把站点的cookie发给黑客。从而达到用受害者身份登录网站进行非法操作的目的。
2.代码实现
分为登录页面admin-c.php,登录成功后的后台页面index-c.php和退出登录logout-c.php。
(1)admin-c.php
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>后台登录</title> <style> * { box-sizing: border-box; } body { margin: 0; min-height: 100vh; background: #f0f2f5; font-family: -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif; } .login-box { /* 关键:脱离文档流,相对视口居中 */ position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10; width: 100%; max-width: 360px; padding: 32px 28px; background: #fff; border-radius: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, .08); } .login-box h1 { margin: 0 0 24px; font-size: 20px; text-align: center; color: #333; } .form-item { margin-bottom: 18px; } .form-item label { display: block; margin-bottom: 6px; font-size: 14px; color: #555; } .form-item input { width: 100%; height: 40px; padding: 0 12px; font-size: 14px; border: 1px solid #dcdfe6; border-radius: 6px; outline: none; transition: border-color .2s; } .form-item input:focus { border-color: #409eff; } .btn { width: 100%; height: 42px; margin-top: 8px; font-size: 15px; color: #fff; background: #409eff; border: none; border-radius: 6px; cursor: pointer; transition: background .2s; } .btn:hover { background: #66b1ff; } </style> </head> <body> <div class="login-box"> <h1>后台管理系统</h1> <!-- 表单:POST 提交 --> <form action="" method="post" autocomplete="off"> <div class="form-item"> <label for="username">账号</label> <input type="text" id="username" name="username" placeholder="请输入账号" required> </div> <div class="form-item"> <label for="password">密码</label> <input type="password" id="password" name="password" placeholder="请输入密码" required> </div> <button type="submit" class="btn">登 录</button> </form> </div> </body> </html> <?php include '../config.php'; //登录文件 /* cookie验证流程 1、接收输入的账号密码 2、判断账号密码正确性 3、正确后生成的cookie进行保存 3.1错误的账号密码进行提示 4、跳转至成功登录的页面 */ //1、接收输入的账号密码 $user=$_POST['username']; $pass=$_POST['password']; //2、判断账号密码正确性 //连接数据库 进行数据库查询将数据进行对比 $sql="select * from admin where username='$user' and password='$pass';"; $data=mysqli_query($con,$sql); //查询数据传给变量$data if($_SERVER["REQUEST_METHOD"]=="POST"){ if(mysqli_num_rows($data)>0){ //mysqli_num_row函数是用来读取数据集有多少行,如果通过sql语句在数据库查询到有这个用户和相应的密码记录,那数据集就不会为0。则判断用户登录成功,继续往下执行。 $expire = time() + 60 * 60 * 24 * 30; // 单位为秒,一个月后过期 setcookie('username', $user, $expire, '/');//把用户名写入cookie setcookie('password', $pass, $expire, '/');//把密码写入cookie echo '<script>alert("登录成功!")</script>'; header('Location: index-c.php');//跳转到index-c.php的页面,也就是登录成功的页面 exit(); }else{ //判断用户登录失败 echo '<script>alert("登录失败!")</script>'; } }(2)Index-c.php
<!--cookie校验,防止没有登陆直接未授权访问该页面--> <?php if($_COOKIE['username']!='admin' and $_COOKIE['password']!='123456'){ //获取cookie中的'username'和'password'字段是否不等于'admin'和'123456',不等于则继续往下执行 header('Location: admin-c.php'); //跳转到登陆页面进行身份校验。 } ?> //登录首页页面 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>后台首页</title> </head> <body> <h1>后台首页</h1> <p>欢迎您,<?php echo $_COOKIE['username']; ?>!</p> <p><a href="logout-c.php">退出登录</a></p> </body> </html>(3)logout-c.php
<?php setcookie('username', '', time() - 3600, '/');//setcookie的第三个参数是过期时间,当前时间减去3600已经是过期了,减去1也可以,都是已经过去时间失效了。 setcookie('password', '', time() - 3600, '/'); // 跳转到登录页面 header('Location: admin-c.php'); exit; ?>3.交作业
登录页面
后台首页
浏览器中存储的cookie内容:
二.Session
1.原理
把服务器想象成一座管理严格的游乐园,登录页 admin-s.php 就是大门口。你递上账号密码,验票员去数据库名单里核对;一旦确认,他并不让你把身份证贴在脑门上到处跑,而是给你手腕套一个独一无二的手环——这就是 Session ID,浏览器通常把它存在 Cookie 里。与此同时,游乐园后台的档案柜里多了一份你的临时档案:你是谁、是不是 admin、密码对不对、能进哪些地方,这些就是 $_SESSION。之后你每走到一个页面,比如 index-s.php,门口保安不看你别的东西,只看手环编号,然后回档案柜里一查:编号有没有、档案在不在、身份对不对。对上了,放行;对不上、手环过期、档案被销毁,或者你从 logout-s.php 出口剪掉手环,就一律被请回大门口重新登录。所以 Session 验证的精髓是:客户端拿的只是一张“临时通行证编号”,真正说了算的身份档案,始终锁在服务器自己手里。
因为真正的凭据内容在服务器端,不像cookie直接放在浏览器,所以比前面的cookie安全一点,存储到服务器,但是增加服务器存储session的成本。
流程图
总体架构图
登录、校验、退出时序图
2.代码实现
(1)admin-s.php
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>后台登录</title> <style> * { box-sizing: border-box; } body { margin: 0; min-height: 100vh; background: #f0f2f5; font-family: -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif; } .login-box { /* 关键:脱离文档流,相对视口居中 */ position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10; width: 100%; max-width: 360px; padding: 32px 28px; background: #fff; border-radius: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, .08); } .login-box h1 { margin: 0 0 24px; font-size: 20px; text-align: center; color: #333; } .form-item { margin-bottom: 18px; } .form-item label { display: block; margin-bottom: 6px; font-size: 14px; color: #555; } .form-item input { width: 100%; height: 40px; padding: 0 12px; font-size: 14px; border: 1px solid #dcdfe6; border-radius: 6px; outline: none; transition: border-color .2s; } .form-item input:focus { border-color: #409eff; } .btn { width: 100%; height: 42px; margin-top: 8px; font-size: 15px; color: #fff; background: #409eff; border: none; border-radius: 6px; cursor: pointer; transition: background .2s; } .btn:hover { background: #66b1ff; } </style> </head> <body> <div class="login-box"> <h1>后台管理系统</h1> <!-- 表单:POST 提交 --> <form action="" method="post" autocomplete="off"> <div class="form-item"> <label for="username">账号</label> <input type="text" id="username" name="username" placeholder="请输入账号" required> </div> <div class="form-item"> <label for="password">密码</label> <input type="password" id="password" name="password" placeholder="请输入密码" required> </div> <button type="submit" class="btn">登 录</button> </form> </div> </body> </html> <?php //登录文件-采用session验证 include '../config.php'; //1、接收输入的账号密码 $user=$_POST['username']; $pass=$_POST['password']; $sql="select * from admin where username='$user' and password='$pass';"; $data=mysqli_query($con,$sql); if($_SERVER["REQUEST_METHOD"]=="POST"){ if(mysqli_num_rows($data)>0){ session_start();//开启session会话 $_SESSION['username']=$user; $_SESSION['password']=$pass; header('Location: index-s.php'); exit(); }else{ echo '<script>alert("登录失败!")</script>'; }}
(2)index-s.php
<?php //判断省去了数据库查询获取账号密码的操作 直接赋值 session_start(); if($_SESSION['username']!='admin' || $_SESSION['password']!='123456'){ //同前面的cookie header('Location: admin-s.php'); } ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>后台首页</title> </head> <body> <h1>后台首页</h1> <p>欢迎您,<?php echo $_SESSION['username']; ?>!</p> <p><a href="logout-s.php">退出登录</a></p> </body> </html>(3)logout-s.php
<?php // 开始会话 session_start(); // 清除 SESSION 变量,并销毁会话 session_unset(); session_destroy(); // 重定向到登录页面 header('Location: admin-s.php'); exit; ?>3.交作业
页面是和cookie一样的,不过服务器多了session文件,PHP study的session文件默认存放位置:phpstudy_pro\phpstudy_pro\Extensions\tmp\tmp
这个位置还可以通过修改配置文件来改变:
phpstudy_pro\phpstudy_pro\Extensions\php\php7.3.4nts\php.ini
在这个文件中搜索session_save_path
session文件里面就是用户和账号密码
浏览器只存了编号
三.token
1.原理
用来唯一标识数据包。你可以把 Token 想象成服务器在用户打开登录页时,悄悄生成并塞进表单里的一张“一次性暗号纸条”,同时服务器自己把这张纸条的底根锁进 Session 保险柜里;当用户填写账号密码点击登录时,表单就会把这张“暗号纸条”一起交回服务器,服务器先看保险柜里有没有底根、有没有过期,再拿出底根和用户交回的纸条用 hash_equals() 仔细核对,只有完全一致且账号密码正确才放行;登录成功后,这张纸条立刻作废销毁,并重新生成 Session ID,就像一次性手环用完即撕,这样即使有人偷看、伪造 Cookie 或想重复提交旧请求,也因为没有服务器保险柜里的底根而无法蒙混过关。
运行机制
3.代码实现
(1)token.php
<?php //生成Token并且将其存储在Session中 session_start(); $token= bin2hex(random_bytes(16)); //将token保存到session中 $_SESSION['token']=$token; $_SESSION['token_time'] = time(); ?> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>后台登录</title> <style> * { box-sizing: border-box; } body { margin: 0; min-height: 100vh; background: #f0f2f5; font-family: -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif; } .login-box { /* 关键:脱离文档流,相对视口居中 */ position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10; width: 100%; max-width: 360px; padding: 32px 28px; background: #fff; border-radius: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, .08); } .login-box h1 { margin: 0 0 24px; font-size: 20px; text-align: center; color: #333; } .form-item { margin-bottom: 18px; } .form-item label { display: block; margin-bottom: 6px; font-size: 14px; color: #555; } .form-item input { width: 100%; height: 40px; padding: 0 12px; font-size: 14px; border: 1px solid #dcdfe6; border-radius: 6px; outline: none; transition: border-color .2s; } .form-item input:focus { border-color: #409eff; } .btn { width: 100%; height: 42px; margin-top: 8px; font-size: 15px; color: #fff; background: #409eff; border: none; border-radius: 6px; cursor: pointer; transition: background .2s; } .btn:hover { background: #66b1ff; } </style> </head> <body> <div class="login-box"> <h1>后台管理系统</h1> <!-- 表单:POST 提交 --> <form action="token_check.php" method="post" autocomplete="off"> <div class="form-item"> <input type="hidden" name="token" value="<?php echo $token;?>"> <label for="username">账号</label> <input type="text" id="username" name="username" placeholder="请输入账号" required> </div> <div class="form-item"> <label for="password">密码</label> <input type="password" id="password" name="password" placeholder="请输入密码" required> </div> <button type="submit" class="btn">登 录</button> </form> </div> </body> </html>(2)Token_check
<?php session_start(); if ($_SERVER['REQUEST_METHOD'] !== 'POST') { exit('非法请求'); } //强制通过post方法提交 $username = $_POST['username'] ?? '';//从 POST 请求中取出 username 参数;如果这个参数不存在,就赋值为空字符串 '' $password = $_POST['password'] ?? ''; $postToken = $_POST['token'] ?? ''; // 1. 检查 session token 是否存在 if (!isset($_SESSION['token'], $_SESSION['token_time'])) { exit('登录失败:token 不存在'); } // 2. 检查 token 是否过期,例如 5 分钟 if (time() - $_SESSION['token_time'] > 300) { unset($_SESSION['token'], $_SESSION['token_time']); exit('登录失败:token 已过期'); } // 3. 比较 POST token 和 SESSION token if (!hash_equals((string)$_SESSION['token'], (string)$postToken)) { exit('登录失败:token 无效'); } // 4. 验证账号密码,实际项目应查数据库并用 password_verify if ($username === 'admin' && $password === '123456') { // token 一次性使用,防止重放 unset($_SESSION['token'], $_SESSION['token_time']); // 防止会话固定 session_regenerate_id(true); echo '登录成功!'; } else { echo '登录失败!'; }3.作业
再次提交表单提示token不存在