使用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注入等。

相关推荐
ZHOUPUYU4 小时前
PHP 8.3网关优化:我用JIT将QPS提升300%的真实踩坑录
开发语言·php
寻寻觅觅☆8 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
l1t8 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
赶路人儿9 小时前
Jsoniter(java版本)使用介绍
java·开发语言
ceclar1239 小时前
C++使用format
开发语言·c++·算法
码说AI10 小时前
python快速绘制走势图对比曲线
开发语言·python
Gofarlic_OMS10 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
星空下的月光影子10 小时前
易语言开发从入门到精通:补充篇·网络爬虫与自动化采集分析系统深度实战·HTTP/HTTPS请求·HTML/JSON解析·反爬策略·电商价格监控·新闻资讯采集
开发语言
老约家的可汗10 小时前
初识C++
开发语言·c++
wait_luky10 小时前
python作业3
开发语言·python