当我们想储存网站用户的信息时,用户登录和注册系统是超级有用的。这适用于从教育网站(可能存储课程进度和分数)到电子商务网站(将存储客户过去购买的信息)的所有内容。
在本教程中,我将教你如何从头开始创建你自己的PHP登录和注册表格。
创建登录和注册表格
我们的第一步将是创建一个登录表单和一个注册表单。这些表单实际上是非常简单的。注册表格将只要求提供一个用户名、电子邮件和密码。用户名和电子邮件对每个注册者来说都是唯一的。如果有人试图用同一个电子邮件地址创建两个账户,我们会向他们显示一个错误信息,让他们知道这个电子邮件已经被使用了。
注册表的编码
这里是创建注册表的HTML。你必须把它放在一个名为register.php的文件中。
<form method="post" action="" name="signup-form">
<div class="form-element">
<label>Username</label>
<input type="text" name="username" pattern="[a-zA-Z0-9]+" required />
</div>
<div class="form-element">
<label>Email</label>
<input type="email" name="email" required />
</div>
<div class="form-element">
<label>Password</label>
<input type="password" name="password" required />
</div>
<button type="submit" name="register" value="register">Register</button>
</form>
这个表单是非常基本的,但我们确实使用了HTML5来做一些非常基本的输入验证。例如,当用户输入的电子邮件地址的格式不正确时,使用type="email"
,会提醒用户。同样地,在用户名上使用pattern
属性将确保用户名只由字母数字字符组成。
登录表单的编码
这里是登录表单的HTML。你可以把它放在一个名为login.php的文件中。
<form method="post" action="" name="signin-form">
<div class="form-element">
<label>Username</label>
<input type="text" name="username" pattern="[a-zA-Z0-9]+" required />
</div>
<div class="form-element">
<label>Password</label>
<input type="password" name="password" required />
</div>
<button type="submit" name="login" value="login">Log In</button>
</form>
用CSS为表单设计样式
这里有一些CSS,你可以应用到这些表单上。
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
margin: 50px auto;
text-align: center;
width: 800px;
}
h1 {
font-family: 'Passion One';
font-size: 2rem;
text-transform: uppercase;
}
label {
width: 150px;
display: inline-block;
text-align: left;
font-size: 1.5rem;
font-family: 'Lato';
}
input {
border: 2px solid #ccc;
font-size: 1.5rem;
font-weight: 100;
font-family: 'Lato';
padding: 10px;
}
form {
margin: 25px auto;
padding: 20px;
border: 5px solid #ccc;
width: 500px;
background: #eee;
}
div.form-element {
margin: 20px 0;
}
button {
padding: 10px;
font-size: 1.5rem;
font-family: 'Lato';
font-weight: 100;
background: yellowgreen;
color: white;
border: none;
}
p.success,
p.error {
color: white;
font-family: lato;
background: yellowgreen;
display: inline-block;
padding: 2px 10px;
}
p.error {
background: orangered;
}
这包含了一些额外的错误信息和标题的样式设计规则。当你创建自己的表单时,本节中的HTML和CSS可以作为你项目的基础,这可能需要不同的样式和输入字段。
创建用户表并连接到数据库
下一步是创建一个用户表,它将存储所有关于注册用户的信息。在我们的例子中,这个表只是由四列组成:
- 一个自动递增的ID
- 一个唯一的用户名
- 一个电子邮件
- 一个密码
你可以使用下面的SQL语句来快速创建该表。
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(25) NOT NULL,
`password` varchar(255) NOT NULL,
`email` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
现在,创建一个名为config.php的文件,并在其中写下以下代码以连接到数据库。
<?php
define('USER', 'root');
define('PASSWORD', '');
define('HOST', 'localhost');
define('DATABASE', 'test');
try {
$connection = new PDO("mysql:host=".HOST.";dbname=".DATABASE, USER, PASSWORD);
} catch (PDOException $e) {
exit("Error: " . $e->getMessage());
}
?>
把数据库名称改为你的数据库的名称。这个文件将被用来建立与数据库的连接。
对用户注册进行编码
现在终于到了实现注册功能的时候了。这段代码的主要功能是检查提供的电子邮件是否已经注册。如果不是,我们就把用户名、电子邮件和密码输入数据库。
将以下代码放在registration.php的顶部。
<?php
session_start();
include('config.php');
if (isset($_POST['register'])) {
$username = $_POST['username'];
$email = $_POST['email'];
$password = $_POST['password'];
$password_hash = password_hash($password, PASSWORD_BCRYPT);
$query = $connection->prepare("SELECT * FROM users WHERE email=:email");
$query->bindParam("email", $email, PDO::PARAM_STR);
$query->execute();
if ($query->rowCount() > 0) {
echo '<p class="error">The email address is already registered!</p>';
}
if ($query->rowCount() == 0) {
$query = $connection->prepare("INSERT INTO users(username,password,email) VALUES (:username,:password_hash,:email)");
$query->bindParam("username", $username, PDO::PARAM_STR);
$query->bindParam("password_hash", $password_hash, PDO::PARAM_STR);
$query->bindParam("email", $email, PDO::PARAM_STR);
$result = $query->execute();
if ($result) {
echo '<p class="success">Your registration was successful!</p>';
} else {
echo '<p class="error">Something went wrong!</p>';
}
}
}
?>
第一步是包括config.php并启动会话。这可以帮助我们存储任何我们想在各个页面保留的信息。
接下来,我们通过检查$_POST['register']
,检查用户是否点击了注册按钮来提交表单。请永远记住,将密码作为纯文本存储并不是一个好主意。出于这个原因,我们使用password_hash()函数,然后将该哈希值存储在我们的数据库中。这个特殊的函数使用随机生成的盐创建了一个60个字符的哈希值。
最后,我们执行查询,并检查一个给定的电子邮件地址是否存在非零行号。如果有,用户会收到一条信息,说这个电子邮件地址已经被注册了。
如果不存在给定电子邮件地址的行,我们就把提供的信息输入数据库,并让用户知道注册成功了。
实现登录功能
在上一步中,我们写了登录用户的代码。这一次,我们简单地检查数据库中的信息,看看输入到表单中的用户名和密码组合是否正确。
下面是login.php顶部的代码。
<?php
session_start();
include('config.php');
if (isset($_POST['login'])) {
$username = $_POST['username'];
$password = $_POST['password'];
$query = $connection->prepare("SELECT * FROM users WHERE username=:username");
$query->bindParam("username", $username, PDO::PARAM_STR);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
if (!$result) {
echo '<p class="error">Username password combination is wrong!</p>';
} else {
if (password_verify($password, $result['password'])) {
$_SESSION['user_id'] = $result['id'];
echo '<p class="success">Congratulations, you are logged in!</p>';
} else {
echo '<p class="error">Username password combination is wrong!</p>';
}
}
}
?>
这里需要注意的一件事是,我们并没有在一个步骤中比较用户名和密码。因为密码实际上是以散列形式存储的,我们首先需要在提供的用户名的帮助下获取散列值。一旦我们得到哈希值,我们就可以使用 password_verify()
函数来比较密码和哈希值。
一旦我们成功确认了密码,我们将$_SESSION['user_id']
变量设置为数据库中该用户的ID。你也可以在这里设置其他变量的值。
限制对页面的访问
大多数要求用户注册的网站都有一些其他页面,用户在这些页面上访问和存储私人数据。你可以使用会话变量来保护这些页面。如果会话变量没有设置,只需将用户重定向到登录页面。否则,向他们展示页面的内容。
<?php
session_start();
if(!isset($_SESSION['user_id'])){
header('Location: login.php');
exit;
} else {
// Show users the page!
}
?>
你唯一要做的是确保脚本在开始时包含session_start()
。
解决常见的错误
在使用这个脚本时,你可能会遇到三种类型的错误。
1.由于变量名不正确而产生的错误
最常见的错误来源之一是某个变量的大写字母不正确。因此,对所有的变量坚持使用相同的命名规则是很重要的。举个例子,$_POST
超级全局中的键是基于分配给表单中输入元素的名称值。这意味着,$_POST['USERNAME']
和$_POST['username']
将有不同的值。
2.2. "标题已发送"错误
一些函数如session_start()
和header()
修改了 HTTP 头信息。由于PHP在输出东西之前会刷新所有的头信息,所以在输出东西之前调用所有这些函数是很重要的。这包括任何原始的HTML或在开头的<?php
标签前无意的空格。
3.会话变量不能跨页保存
只有当你在一个页面上调用了函数session_start()
,你才能在该页面上访问会话变量。如果你不能在一个页面上访问$_SESSION
超全局中的值,这可能是因为你忘记了调用session_start()
。还要记得在你在页面上输出任何东西之前调用该函数。否则,你会遇到 "Headers already sent" 的错误。