如何基于PostgreSQL和PHP实现注册用户名重复校验功能
注册环节用户名重复校验实现方案
首先要先给数据库register表的username字段加唯一约束,从数据库层面阻止重复用户名插入,避免并发请求下的脏数据:
ALTER TABLE register ADD CONSTRAINT unique_username UNIQUE (username);
基础实现:表单提交后页内显示错误提示
你原来的代码把表单页和处理逻辑分开,没法直接在原页显示提示,最简便的改法是把两个部分合并到同一个sign_up.php文件里,通过变量传递错误信息,直接在页面渲染提示。
另外原代码有两个必须修复的问题:
- 调用
header()做跳转后没有加exit终止执行,后续的数据库插入逻辑依然会运行 - 直接把用户输入拼接进SQL语句,存在严重的SQL注入风险,需要改用参数化查询
修正后的完整sign_up.php代码如下:
<?php $errorMsg = ''; $host = "localhost"; $dbusername = "postgres"; $dbpassword = "admroot"; $db = "local_db_server_test"; $con = pg_connect("host=$host dbname=$db user=$dbusername password=$dbpassword") or die ("Could not connect to Server\n"); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $username = trim($_POST['username']); $password = $_POST['password']; if (strlen($password) < 6) { pg_close($con); header("Location:sign_up_pass_err.html"); exit; } // 校验用户名是否已存在 $checkSql = "SELECT 1 FROM register WHERE username = $1"; $checkRes = pg_query_params($con, $checkSql, [$username]); if (pg_num_rows($checkRes) > 0) { $errorMsg = "username is already taken"; pg_free_result($checkRes); } else { // 校验通过,执行插入 $insertSql = "INSERT INTO register(username, password) VALUES ($1, crypt($2, gen_salt('md5')))"; $insertRes = pg_query_params($con, $insertSql, [$username, $password]); if ($insertRes) { pg_close($con); header("Location: login.html"); exit; } else { $errorMsg = "Registration failed, please try again later"; } } } pg_close($con); ?> <!DOCTYPE html> <html> <head> <style>.err-tip { color: #f00; margin: 6px 0; font-size: 14px; }</style> </head> <body> <form action="sign_up.php" method="post"> <input type="text" name="username" placeholder="Username" required value="<?php echo isset($_POST['username']) ? htmlspecialchars($_POST['username']) : ''; ?>"> <br> <?php if ($errorMsg): ?> <div class="err-tip"><?php echo htmlspecialchars($errorMsg); ?></div> <?php endif; ?> <br> <input type="password" name="password" placeholder="Password" required><br><br> <input type="submit" value="Sign up"> </form> </body> </html>
这个实现的逻辑很直接:
- 提交表单后先查库判断用户名是否存在,存在就把错误信息存到
$errorMsg变量 - 页面渲染时如果
$errorMsg不为空,直接把提示显示在用户名输入框下方 - 校验失败时自动保留用户之前输入的用户名,不用重复填写
- 所有输出到页面的动态内容都用
htmlspecialchars转义,避免XSS漏洞
优化方案:输入时实时异步校验
如果需要提升体验,让用户输入完用户名不用点提交就知道是否被占用,可以加前端异步校验逻辑。
注意:前端异步校验仅用于提升用户体验,表单提交时的后端重复校验必须保留,前端校验逻辑可以被随意绕过,不能作为唯一的校验依据。
- 新增
check_username.php校验接口:
<?php $host = "localhost"; $dbusername = "postgres"; $dbpassword = "admroot"; $db = "local_db_server_test"; $con = pg_connect("host=$host dbname=$db user=$dbusername password=$dbpassword"); $res = ['exists' => false]; if (isset($_GET['username']) && trim($_GET['username'])) { $username = trim($_GET['username']); $checkSql = "SELECT 1 FROM register WHERE username = $1"; $query = pg_query_params($con, $checkSql, [$username]); $res['exists'] = pg_num_rows($query) > 0; } header('Content-Type: application/json'); echo json_encode($res); pg_close($con); ?>
- 在注册页
</body>标签前加入JS逻辑,监听用户名输入框失焦事件:
<script> const usernameInput = document.querySelector('input[name="username"]'); const tipDom = document.querySelector('.err-tip'); usernameInput.addEventListener('blur', async () => { const uname = usernameInput.value.trim(); if (!uname) return; const resp = await fetch(`check_username.php?username=${encodeURIComponent(uname)}`); const data = await resp.json(); tipDom.textContent = data.exists ? 'username is already taken' : ''; }) </script>
内容的提问来源于stack exchange,提问作者Fors1t
相关产品推荐
相关产品推荐

