使用PHP和Ajax实现登录表单“记住我”功能的页面刷新问题排查
解决“记住我”功能无法持久化登录状态的问题
我看了你的代码,发现几个核心问题导致“记住我”功能不生效,而且还有严重的安全隐患,咱们一步步来修复:
一、首要安全问题:绝对不要在Cookie中存储明文密码
你现在直接把用户的明文密码存在Cookie里,这是非常危险的——任何人只要拿到这个Cookie就能冒充用户登录。正确的做法是生成一个唯一、不可预测的令牌(token),同时存在数据库和Cookie中,完全避开存储密码的操作。
二、逻辑缺陷:勾选“记住我”时未初始化Session,且入口页未处理Cookie自动登录
当前代码里,当用户勾选“记住我”时,你只设置了Cookie但没有创建Session,而跳转后的patient-details.php大概率依赖Session判断登录状态。另外,登录入口页只检查了Session或Cookie的存在,但没有在Cookie有效时自动完成登录流程,导致Cookie存在也无法直接进入系统。
三、Cookie路径问题
setcookie的第四个参数(路径)你传的是空字符串,这会导致Cookie只在当前目录有效,如果你的登录页在子目录,跳转后可能无法读取到Cookie。建议设置为"/",让全站都能访问。
修改后的代码示例
1. 数据库新增字段(用于存储记住我令牌)
先给tbl_patients表新增两个字段:
remember_token:字符串类型,长度建议64,用于存储唯一令牌remember_token_expiry:datetime类型,存储令牌过期时间
2. 登录页(HTML)
新增Cookie自动登录逻辑,确保用户下次访问时能直接通过Cookie验证:
<!DOCTYPE html> <?php session_start(); // 新增:如果存在有效的记住我Cookie,自动完成登录 if(isset($_COOKIE['remember_token'])){ $conn = new mysqli("localhost", "root", "", "mydb") or die(mysqli_error()); $token = mysqli_real_escape_string($conn, $_COOKIE['remember_token']); $sql = "SELECT * FROM tbl_patients WHERE remember_token = '$token' AND remember_token_expiry > NOW()"; $result = mysqli_query($conn, $sql); if(mysqli_num_rows($result) == 1){ $row = mysqli_fetch_assoc($result); $_SESSION['patient_id'] = $row['patient_id']; $_SESSION['username'] = $row['username']; header('location: patient-details.php'); exit; } } // 原有登录状态检查 if( isset($_SESSION['patient_id']) ) { header('location: patient-details.php'); exit; } ?> <html lang="en"> <head> </head> <body class="bg-gradient-primary"> <div class="container"> <!-- Outer Row --> <div class="row justify-content-center"> <div class="col-xl-5 col-lg-3 col-md-3"> <div class="card o-hidden border-0 shadow-lg my-5"> <div class="card-body p-0"> <!-- Nested Row within Card Body --> <div class="row"> <div class="col-lg-12"> <div class="p-5"> <div class="text-center"> <h1 class="h4 text-gray-900 mb-4">Kabi Medical Records</h1> <h7 class="h7 text-gray-900 mb-4">Log in to continue.</h7> </div> <form class="user" method = "POST" id="loginform"> <hr> <div id="loginmessage"></div> <div class="form-group"> <input type="text" class="form-control form-control-user" id="UserName" aria-describedby="username" placeholder="User Name" name="username" value=""> </div> <div class="form-group"> <input type="password" class="form-control form-control-user" id="UserPassword" placeholder="Password" name="password" value=""> </div> <div class="form-group"> <div class="custom-control custom-checkbox small"> <input type="checkbox" class="custom-control-input" id="customCheck" name="remember" value="yes"> <label class="custom-control-label" for="customCheck">Remember Me</label> </div> </div> <button type="submit" id="userloginbtn" class="btn btn-primary btn-user btn-block" name="login">Login</button> </form> <hr> <div class="text-center"> <a class="small" href="#">Forgot Password?</a> </div> <div class="text-center"> <a class="small" href="/kabirecords">Back to Home</a> </div> </div> </div> </div> </div> </div> </div> </div> </div> <script src="vendor/jquery/jquery.min.js"></script> <script src="assets/js/l.js"></script> </body> </html>
3. Login.php(处理登录请求)
修改Cookie逻辑为令牌模式,同时确保无论是否勾选“记住我”都初始化Session:
<?php //Start session session_start(); //Connect to the database $conn = new mysqli("localhost", "root", "", "mydb") or die(mysqli_error()); // 防止SQL注入,转义用户输入 $username = mysqli_real_escape_string($conn, trim($_POST['username'])); $password = mysqli_real_escape_string($conn, trim($_POST['password'])); $remember = isset($_POST['remember']) ? $_POST['remember'] : "no"; //Run query: Check combinaton of username & password exists $sql = "SELECT * FROM tbl_patients WHERE username='$username' && password='$password'"; $result = mysqli_query($conn, $sql) or die(mysqli_error()); if(!empty($username) && !empty($password)) { $count = mysqli_num_rows($result); if ($count !== 1) { echo '<div class="alert alert-danger">Wrong Username or Password</div>'; } else { $row = mysqli_fetch_array($result, MYSQLI_ASSOC); // 不管是否记住我,都初始化Session保证当前登录有效 $_SESSION['patient_id'] = $row['patient_id']; $_SESSION['username'] = $row['username']; if($remember == "yes") { // 生成32字节的随机令牌,转为16进制字符串 $token = bin2hex(random_bytes(32)); $expiry = date('Y-m-d H:i:s', time() + (60*60*24*7)); // 7天后过期 // 更新数据库中的令牌和过期时间 $update_sql = "UPDATE tbl_patients SET remember_token = '$token', remember_token_expiry = '$expiry' WHERE patient_id = " . $row['patient_id']; mysqli_query($conn, $update_sql); // 设置Cookie,路径为"/"确保全站可访问 setcookie('remember_token', $token, time()+(60*60*24*7), "/"); } else { // 如果不勾选,清除可能存在的记住我Cookie if(isset($_COOKIE['remember_token'])){ setcookie('remember_token', "", time()-3600, "/"); // 同时清空数据库中的令牌 $update_sql = "UPDATE tbl_patients SET remember_token = NULL, remember_token_expiry = NULL WHERE patient_id = " . $row['patient_id']; mysqli_query($conn, $update_sql); } } echo "success"; } } else { echo '<div class="alert alert-danger">Both are Required Fields </div>'; } ?>
4. Login.js(保持原有逻辑,无需修改)
$("#loginform").submit(function(event) { //prevent default php processing event.preventDefault(); //collect user inputs var datatopost = $(this).serializeArray(); //send them to login.php using AJAX $.ajax({ url: "login.php", type: "POST", data: datatopost, success: function(data) { if ($.trim(data) === "success") { Swal.fire({ icon: 'success', title: 'Login successful', }); window.location = "patient-details.php"; } else { $("#loginmessage").html(data); } }, error: function() { $("#loginmessage").html( "<div class='alert alert-danger'>There was an error with the Ajax Call. Please try again later.</div>" ); } }); });
额外优化建议
- 密码存储优化:现在你直接存储明文密码在数据库里,这也是严重的安全问题,建议使用
password_hash()加密密码,用password_verify()验证密码。 - SQL注入防护:我在代码里加了
mysqli_real_escape_string(),但更推荐使用预处理语句(Prepared Statements)来彻底防止SQL注入。 - Cookie安全属性:可以给
setcookie添加httponly和secure属性,增强安全性:setcookie('remember_token', $token, time()+(60*60*24*7), "/", "", true, true);(secure需要HTTPS环境)。
内容的提问来源于stack exchange,提问作者Nguyano
相关产品推荐
相关产品推荐

