使用PHP和JWT实现安全的API身份验证

目录

[1. JWT简介](#1. JWT简介)

[2. 环境准备](#2. 环境准备)

[3. 用户注册与登录](#3. 用户注册与登录)

[3.1 数据库设计](#3.1 数据库设计)

[3.2 注册接口](#3.2 注册接口)

[3.3 登录接口](#3.3 登录接口)

[4. 验证JWT](#4. 验证JWT)

[5. 安全性考虑](#5. 安全性考虑)

[6. 总结](#6. 总结)


1. JWT简介

JWT由三部分组成:

Header -包含令牌的类型和使用的哈希算法(如HMAC SHA256或RSA)。
Payload -包含声明(claims)。声明是关于实体(通常是用户)和其他数据的,有三种类型:注册声明、公共声明和私有声明。
Signature-对前两部分的签名,防止数据篡改。

例如,一个JWT看起来像这样:`xxxxx.yyyyy.zzzzz`(三个Base64-URL字符串用点分隔)。

2. 环境准备

在开始之前,确保你已经安装了PHP(建议7.4以上版本)和Composer。我们这里使用`firebase/php-jwt`库来处理JWT的生成和验证。

复制代码
composer require firebase/php-jwt

3. 用户注册与登录

3.1 数据库设计

为了简单起见,我们使用MySQL数据库。创建一个名为users的表:

复制代码
CREATE TABLE `users` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `username` VARCHAR(50) NOT NULL UNIQUE,
  `password` VARCHAR(255) NOT NULL
);

注意: ‌ 密码必须是经过哈希处理,用到的是PHP的password_hash()函数。

3.2 注册接口

在注册接口中,我们接收用户名和密码,将密码哈希后存储到数据库。

复制代码
<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;

// 连接数据库
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'password');

function register($username, $password) {
    global $pdo;
    // 检查用户名是否已存在
    $stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
    $stmt->execute([$username]);
    if ($stmt->fetch()) {
        return ['error' => 'User already exists'];
    }
    // 哈希密码
    $hash = password_hash($password, PASSWORD_DEFAULT);
    $stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
    $stmt->execute([$username, $hash]);
    return ['message' => 'User registered successfully'];
}

3.3 登录接口

登录时,验证用户名和密码,如果正确,则生成JWT令牌返回给客户端。

复制代码
function login($username, $password) {
    global $pdo;
    $stmt = $pdo->prepare("SELECT id, password FROM users WHERE username = ?");
    $stmt->execute([$username]);
    $user = $stmt->fetch();
    if (!$user || !password_verify($password, $user['password'])) {
        return ['error' => 'Invalid credentials'];
    }

    // 生成JWT
    $secret_key = "your_secret_key"; // 应该是一个复杂的随机字符串,存储在环境变量中
    $payload = [
        'iss' => 'localhost', // 签发者
        'aud' => 'localhost', // 接收方
        'iat' => time(), // 签发时间
        'exp' => time() + 3600, // 过期时间(1小时)
        'data' => [
            'user_id' => $user['id'],
            'username' => $username
        ]
    ];
    $jwt = JWT::encode($payload, $secret_key, 'HS256');
    return ['token' => $jwt];
}

4. 验证JWT

在需要身份验证的API(例如用户个人信息接口)中,我们需要验证客户端传来的JWT。

复制代码
function getProfile() {
    // 获取Authorization头
    $headers = getallheaders();
    if (!isset($headers['Authorization'])) {
        http_response_code(401);
        return ['error' => 'Token not provided'];
    }
    $token = str_replace('Bearer ', '', $headers['Authorization']);
    $secret_key = "your_secret_key";

    try {
        $decoded = JWT::decode($token, new Firebase\JWT\Key($secret_key, 'HS256'));
        // 如果验证成功,$decoded将包含我们之前设置的payload
        $user_id = $decoded->data->user_id;
        // 现在可以根据user_id从数据库获取用户信息并返回
        // ... 数据库查询代码 ...
        return ['user' => $userInfo];
    } catch (Exception $e) {
        http_response_code(401);
        return ['error' => 'Invalid token'];
    }
}

5. 安全性考虑

  • 使用HTTPS‌:在传输过程中保护JWT,防止被截获。
  • 密钥安全 ‌:密钥(secret_key)必须足够复杂,且不要硬编码在代码中,应该使用环境变量。
  • 令牌过期‌:设置合理的过期时间(如1小时),并考虑使用刷新令牌机制来延长会话。
  • 存储方式‌:客户端通常将JWT存储在localStorage或sessionStorage中,但要注意XSS攻击。也可以使用HttpOnly的Cookie来存储,避免XSS,但要注意CSRF防护。

6. 总结

通过本文,我们学习了如何在PHP中使用JWT进行API身份验证。从用户注册登录到JWT的生成与验证,我们实现了一套完整的流程。JWT提供了一种无状态的认证机制,非常适合分布式应用和前后端分离架构。当然,安全是一个持续的过程,除了本文介绍的内容,我们平时开发的时候还是需要关注其他安全措施,比如常见的输入验证、基础防止SQL注入等。

相关推荐
Scott9999HH4 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社5 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海5 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖6 小时前
JDK 26 新特性详解
java·开发语言
马优晨7 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区8 小时前
怎么把C语言学到精通?
c语言·开发语言
九硕智慧建筑一体化厂家9 小时前
直流智能照明|一站式低碳照明解决方案,安全、节能、降本、全场景适配
安全
心平气和量大福大9 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai9 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜9 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript