如何使用php实现用户登录

发布时间:2021-09-14 10:31:32 作者:柒染
来源:亿速云 阅读:158
# 如何使用PHP实现用户登录

## 前言

用户登录系统是Web开发中最基础也最重要的功能之一。无论是电商网站、社交平台还是内容管理系统,几乎所有的Web应用都需要用户认证机制。本文将详细介绍如何使用PHP实现一个完整的用户登录系统,涵盖从数据库设计到前端交互的全过程。

## 目录

1. [系统需求分析](#系统需求分析)
2. [数据库设计与实现](#数据库设计与实现)
3. [前端登录表单设计](#前端登录表单设计)
4. [PHP登录逻辑实现](#php登录逻辑实现)
5. [会话管理与安全性](#会话管理与安全性)
6. [密码加密与验证](#密码加密与验证)
7. [记住我功能实现](#记住我功能实现)
8. [登录限制与防护](#登录限制与防护)
9. [错误处理与用户体验](#错误处理与用户体验)
10. [完整代码示例](#完整代码示例)
11. [总结与扩展](#总结与扩展)

---

## 系统需求分析

在开始编码之前,我们需要明确登录系统的基本需求:

1. **用户凭证存储**:需要存储用户名/邮箱和密码
2. **身份验证**:验证用户提交的凭证是否正确
3. **会话管理**:登录后保持用户状态
4. **安全性**:
   - 密码加密存储
   - 防止SQL注入
   - CSRF防护
   - 暴力破解防护
5. **用户体验**:
   - 友好的错误提示
   - "记住我"功能
   - 密码找回功能(本文不涉及)

---

## 数据库设计与实现

### 用户表结构设计

```sql
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `email` varchar(100) NOT NULL,
  `password_hash` varchar(255) NOT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `last_login` datetime DEFAULT NULL,
  `is_active` tinyint(1) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

数据库连接

创建config/database.php文件:

<?php
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', 'password');
define('DB_NAME', 'auth_demo');

function getDBConnection() {
    try {
        $dsn = "mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=utf8mb4";
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
        ];
        return new PDO($dsn, DB_USER, DB_PASS, $options);
    } catch (PDOException $e) {
        die("Database connection failed: " . $e->getMessage());
    }
}

前端登录表单设计

基础HTML结构

<!-- login.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>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        <div class="row justify-content-center">
            <div class="col-md-6 col-lg-4">
                <div class="card shadow">
                    <div class="card-body">
                        <h3 class="card-title text-center mb-4">用户登录</h3>
                        
                        <form id="loginForm" action="authenticate.php" method="POST">
                            <!-- CSRF令牌 -->
                            <input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
                            
                            <div class="mb-3">
                                <label for="username" class="form-label">用户名/邮箱</label>
                                <input type="text" class="form-control" id="username" name="username" required>
                            </div>
                            
                            <div class="mb-3">
                                <label for="password" class="form-label">密码</label>
                                <input type="password" class="form-control" id="password" name="password" required>
                            </div>
                            
                            <div class="mb-3 form-check">
                                <input type="checkbox" class="form-check-input" id="remember" name="remember">
                                <label class="form-check-label" for="remember">记住我</label>
                            </div>
                            
                            <button type="submit" class="btn btn-primary w-100">登录</button>
                        </form>
                        
                        <div class="mt-3 text-center">
                            <a href="forgot_password.php">忘记密码?</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

PHP登录逻辑实现

认证处理脚本

创建authenticate.php

<?php
session_start();
require_once 'config/database.php';
require_once 'includes/functions.php';

// 验证CSRF令牌
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    die("CSRF token validation failed");
}

// 获取用户输入
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
$remember = isset($_POST['remember']);

// 基本验证
if (empty($username) || empty($password)) {
    redirectWithError('用户名和密码不能为空');
}

// 数据库查询
try {
    $db = getDBConnection();
    $stmt = $db->prepare("SELECT * FROM users WHERE username = :username OR email = :username");
    $stmt->bindParam(':username', $username);
    $stmt->execute();
    $user = $stmt->fetch();
    
    if (!$user) {
        logFailedAttempt($username);
        redirectWithError('用户名或密码错误');
    }
    
    // 验证密码
    if (!password_verify($password, $user['password_hash'])) {
        logFailedAttempt($username);
        redirectWithError('用户名或密码错误');
    }
    
    // 检查账户是否激活
    if (!$user['is_active']) {
        redirectWithError('您的账户已被禁用,请联系管理员');
    }
    
    // 登录成功处理
    handleSuccessfulLogin($user, $remember);
    
} catch (PDOException $e) {
    error_log("Login error: " . $e->getMessage());
    redirectWithError('系统错误,请稍后再试');
}

// 重定向到受保护页面
header("Location: dashboard.php");
exit;

会话管理与安全性

会话初始化

创建includes/session.php

<?php
// 安全会话配置
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1); // 仅在HTTPS下使用
ini_set('session.use_strict_mode', 1);

// 自定义会话名称
session_name('AUTHSESSID');

// 启动会话
session_start();

// 定期更换会话ID
if (!isset($_SESSION['created'])) {
    $_SESSION['created'] = time();
} elseif (time() - $_SESSION['created'] > 1800) {
    session_regenerate_id(true);
    $_SESSION['created'] = time();
}

// 检查用户是否已登录
function isUserLoggedIn() {
    return isset($_SESSION['user_id']) && !empty($_SESSION['user_id']);
}

// 获取当前用户ID
function getCurrentUserId() {
    return $_SESSION['user_id'] ?? null;
}

// 生成CSRF令牌
function generateCSRFToken() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

密码加密与验证

密码处理函数

// includes/auth_functions.php

/**
 * 创建密码哈希
 */
function createPasswordHash($password) {
    return password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
}

/**
 * 验证密码
 */
function verifyPassword($password, $hash) {
    return password_verify($password, $hash);
}

/**
 * 检查密码是否需要重新哈希
 */
function needsRehash($hash) {
    return password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12]);
}

/**
 * 生成随机密码
 */
function generateRandomPassword($length = 12) {
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()';
    $password = '';
    for ($i = 0; $i < $length; $i++) {
        $password .= $chars[random_int(0, strlen($chars) - 1)];
    }
    return $password;
}

记住我功能实现

持久登录令牌

  1. 创建remember_tokens表:
CREATE TABLE `remember_tokens` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `token` varchar(64) NOT NULL,
  `expires_at` datetime NOT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `token` (`token`),
  KEY `user_id` (`user_id`),
  CONSTRNT `remember_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 实现记住我功能:
// includes/remember_me.php

function setRememberMeCookie($userId) {
    $token = bin2hex(random_bytes(32));
    $expires = time() + 60 * 60 * 24 * 30; // 30天
    
    // 存储到数据库
    $db = getDBConnection();
    $stmt = $db->prepare("INSERT INTO remember_tokens (user_id, token, expires_at) VALUES (?, ?, ?)");
    $stmt->execute([$userId, $token, date('Y-m-d H:i:s', $expires)]);
    
    // 设置cookie
    setcookie(
        'remember_me',
        $token,
        $expires,
        '/',
        '',
        true,  // 仅HTTPS
        true   // HttpOnly
    );
}

function checkRememberMeToken() {
    if (isset($_COOKIE['remember_me']) && !isUserLoggedIn()) {
        $token = $_COOKIE['remember_me'];
        
        $db = getDBConnection();
        $stmt = $db->prepare("SELECT user_id FROM remember_tokens WHERE token = ? AND expires_at > NOW()");
        $stmt->execute([$token]);
        $result = $stmt->fetch();
        
        if ($result) {
            $userId = $result['user_id'];
            $_SESSION['user_id'] = $userId;
            
            // 更新令牌有效期
            $newExpires = time() + 60 * 60 * 24 * 30;
            $stmt = $db->prepare("UPDATE remember_tokens SET expires_at = ? WHERE token = ?");
            $stmt->execute([date('Y-m-d H:i:s', $newExpires), $token]);
            
            setcookie(
                'remember_me',
                $token,
                $newExpires,
                '/',
                '',
                true,
                true
            );
        }
    }
}

登录限制与防护

登录尝试限制

  1. 创建login_attempts表:
CREATE TABLE `login_attempts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ip_address` varchar(45) NOT NULL,
  `username` varchar(255) NOT NULL,
  `attempt_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `ip_address` (`ip_address`),
  KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 实现登录限制:
// includes/security.php

function logFailedAttempt($username) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $db = getDBConnection();
    
    $stmt = $db->prepare("INSERT INTO login_attempts (ip_address, username) VALUES (?, ?)");
    $stmt->execute([$ip, $username]);
}

function isLoginBlocked($username) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $db = getDBConnection();
    
    // 检查IP限制
    $stmt = $db->prepare("SELECT COUNT(*) as attempts FROM login_attempts 
                         WHERE ip_address = ? AND attempt_time > DATE_SUB(NOW(), INTERVAL 1 HOUR)");
    $stmt->execute([$ip]);
    $ipAttempts = $stmt->fetch()['attempts'];
    
    // 检查用户名限制
    $stmt = $db->prepare("SELECT COUNT(*) as attempts FROM login_attempts 
                         WHERE username = ? AND attempt_time > DATE_SUB(NOW(), INTERVAL 1 HOUR)");
    $stmt->execute([$username]);
    $userAttempts = $stmt->fetch()['attempts'];
    
    // 如果IP或用户名尝试次数超过5次,则阻止登录
    return $ipAttempts >= 5 || $userAttempts >= 5;
}

function clearLoginAttempts($username) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $db = getDBConnection();
    
    $stmt = $db->prepare("DELETE FROM login_attempts WHERE ip_address = ? OR username = ?");
    $stmt->execute([$ip, $username]);
}

错误处理与用户体验

友好的错误处理

// includes/functions.php

function redirectWithError($message) {
    $_SESSION['login_error'] = $message;
    header("Location: login.php");
    exit;
}

function displayError() {
    if (isset($_SESSION['login_error'])) {
        echo '<div class="alert alert-danger">' . htmlspecialchars($_SESSION['login_error']) . '</div>';
        unset($_SESSION['login_error']);
    }
}

// 在登录表单中添加错误显示
<div class="mb-3">
    <?php displayError(); ?>
</div>

完整代码示例

整合所有功能

// authenticate.php (完整版)

<?php
require_once 'config/database.php';
require_once 'includes/functions.php';
require_once 'includes/session.php';
require_once 'includes/auth_functions.php';
require_once 'includes/security.php';
require_once 'includes/remember_me.php';

// 检查登录尝试限制
if (isLoginBlocked($_POST['username'] ?? '')) {
    redirectWithError('登录尝试次数过多,请1小时后再试');
}

// CSRF验证
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    redirectWithError('无效的请求');
}

// 获取输入
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
$remember = isset($_POST['remember']);

// 验证输入
if (empty($username) || empty($password)) {
    redirectWithError('用户名和密码不能为空');
}

try {
    $db = getDBConnection();
    $stmt = $db->prepare("SELECT * FROM users WHERE username = :username OR email = :username");
    $stmt->bindParam(':username', $username);
    $stmt->execute();
    $user = $stmt->fetch();
    
    if (!$user || !verifyPassword($password, $user['password_hash'])) {
        logFailedAttempt($username);
        redirectWithError('用户名或密码错误');
    }
    
    if (!$user['is_active']) {
        redirectWithError('您的账户已被禁用');
    }
    
    // 登录成功
    $_SESSION['user_id'] = $user['id'];
    $_SESSION['user_name'] = $user['username'];
    
    // 更新最后登录时间
    $stmt = $db->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
    $stmt->execute([$user['id']]);
    
    // 清除失败的登录尝试
    clearLoginAttempts($username);
    
    // 记住我功能
    if ($remember) {
        setRememberMeCookie($user['id']);
    }
    
    // 重定向到目标页面
    $redirect = $_SESSION['redirect_url'] ?? 'dashboard.php';
    unset($_SESSION['redirect_url']);
    header("Location: " . $redirect);
    exit;
    
} catch (PDOException $e) {
    error_log("Login error: " . $e->getMessage());
    redirectWithError('系统错误,请稍后再试');
}

总结与扩展

实现总结

我们完成了一个完整的PHP登录系统,包含以下功能:

  1. 安全的数据库存储
  2. 密码加密与验证
  3. 会话管理
  4. CSRF防护
  5. 登录尝试限制
  6. “记住我”功能
  7. 友好的错误处理

扩展功能建议

  1. 双因素认证:集成Google Authenticator或短信验证
  2. 密码强度检查:在注册和密码修改时验证密码强度
  3. 登录日志:记录用户的登录时间和IP
  4. 账户锁定:多次失败后自动锁定账户
  5. OAuth集成:支持第三方登录(Google, Facebook等)

安全最佳实践

  1. 始终使用HTTPS
  2. 定期更新依赖库
推荐阅读:
  1. php+ajax如何实现用户登录按钮加载
  2. 使用django怎么实现用户登录验证

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

php

上一篇:CSS文本格式的简单介绍

下一篇:php中header如何设置编码

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》