WordPress源码深度解析之:`WordPress`的`REST API`:如何利用`JWT`进行无状态认证。

各位观众老爷们,晚上好!欢迎来到“WordPress源码深度解析”特别节目,我是今晚的主讲人,一个平平无奇的码农。今天咱们不聊情怀,只聊代码,深入挖掘WordPress的REST API,尤其是如何利用JWT实现安全又优雅的无状态认证。

开场白:为什么我们需要JWT?

在传统的Session认证中,服务端需要记录用户的登录状态,这在单体应用中问题不大,但到了分布式系统或RESTful API场景,Session共享就成了一个难题。我们需要一个更轻量、更无状态的认证方案,而JWT(JSON Web Token)就是那个天选之子。

JWT就像一张通行证,服务端验证通过后,发放给客户端。客户端每次请求都带着这张通行证,服务端只需要验证通行证的真伪,无需每次都查数据库,大大减轻了服务器的压力。

第一幕:WordPress REST API 概览

WordPress 从 4.7 版本开始,内置了 REST API。它允许开发者通过 HTTP 请求(GET, POST, PUT, DELETE)来操作 WordPress 的数据,比如文章、页面、用户等等。

访问 REST API 的基本 URL 结构是:

http(s)://your-wordpress-site.com/wp-json/wp/v2/posts
  • /wp-json:这是 REST API 的入口点。
  • /wp/v2:这是 API 的版本号。
  • /posts:这是资源端点,这里是文章。

如果你直接访问这个 URL,你会看到一个 JSON 格式的文章列表(当然,前提是你没有设置权限)。

第二幕:JWT 实战准备

要让 WordPress REST API 支持 JWT 认证,我们需要借助插件的力量。这里推荐使用JWT Authentication for WP REST API插件。

  1. 安装插件: 在 WordPress 后台搜索并安装 JWT Authentication for WP REST API 插件。
  2. 配置插件: 安装完成后,在 WordPress 设置中找到 JWT Authentication 设置。你需要配置一个密钥(Secret Key),这个密钥用于签名和验证 JWT。务必选择一个足够安全、足够长的密钥,并妥善保管。
// 建议的密钥生成方式
<?php
echo bin2hex(random_bytes(32)); // 生成一个 64 位的十六进制密钥
?>

将生成的密钥复制到插件的 Secret Key 设置中。

第三幕:JWT 的工作原理

JWT 本身是一个字符串,由三部分组成,用点号(.)分隔:

  1. Header(头部): 包含 JWT 的类型(JWT)和使用的签名算法(例如 HS256)。
  2. Payload(载荷): 包含声明(claims),即关于用户或其他数据的断言。 可以分为三种声明类型:
    • Registered claims: 预定义的声明,如 iss (issuer)、sub (subject)、aud (audience)、exp (expiration time)、iat (issued at time)、jti (JWT ID)。
    • Public claims: 可以自定义的声明,但为了避免冲突,建议使用 IANA 注册的命名空间或 URI 作为前缀。
    • Private claims: 自定义的声明,仅在发送者和接收者之间约定使用。
  3. Signature(签名): 使用 Header 中指定的算法和密钥对 Header 和 Payload 进行签名,用于验证 JWT 的完整性和真实性。

举个栗子:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  • Header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 (base64url 解码后: {"alg":"HS256","typ":"JWT"})
  • Payload: eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ (base64url 解码后: {"sub":"1234567890","name":"John Doe","iat":1516239022})
  • Signature: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

第四幕:获取 JWT Token

安装并配置好插件后,你需要通过一个特定的端点来获取 JWT Token。默认情况下,这个端点是:

http(s)://your-wordpress-site.com/wp-json/jwt-auth/v1/token

你需要发送一个 POST 请求到这个端点,携带用户名和密码:

POST /wp-json/jwt-auth/v1/token HTTP/1.1
Content-Type: application/json

{
  "username": "your_username",
  "password": "your_password"
}

如果用户名和密码正确,服务器会返回一个 JSON 响应,包含 JWT Token:

{
  "token": "your_jwt_token",
  "user_email": "[email protected]",
  "user_nicename": "your_username",
  "user_display_name": "Your Display Name"
}

第五幕:使用 JWT Token 访问受保护的 API

拿到 JWT Token 后,你就可以用它来访问受保护的 API 端点了。你需要将 Token 放在请求头中,使用 Authorization 字段,并以 Bearer 开头:

GET /wp-json/wp/v2/posts HTTP/1.1
Authorization: Bearer your_jwt_token

如果没有提供正确的 Token,或者 Token 已过期,服务器会返回 401 Unauthorized 错误。

第六幕:JWT 插件源码分析(核心部分)

让我们扒一扒 JWT Authentication for WP REST API 插件的核心源码,看看它是如何实现 JWT 认证的。

  • class JWT_AUTH 这是插件的核心类,负责处理 JWT 的生成、验证和认证。

  • generate_token( $user ) 这个方法负责生成 JWT Token。它会创建一个包含用户信息的 Payload,然后使用密钥进行签名。

    public function generate_token( $user ) {
        $issued_at   = time();
        $not_before  = apply_filters( 'jwt_auth_not_before', $issued_at, $issued_at );
        $expire      = apply_filters( 'jwt_auth_expire', $issued_at + ( DAY_IN_SECONDS * 7 ), $issued_at ); // 默认7天过期
        $token_id    = base64_encode( openssl_random_pseudo_bytes( 32 ) );
    
        $data = array(
            'iss' => get_bloginfo( 'url' ),
            'iat' => $issued_at,
            'nbf' => $not_before,
            'exp' => $expire,
            'jti' => $token_id,
            'data' => array(
                'user' => array(
                    'id' => $user->ID,
                ),
            ),
        );
    
        $data = apply_filters( 'jwt_auth_token_before_sign', $data, $user );
    
        $jwt = JWT::encode(
            $data,
            $this->secret_key,
            'HS256'
        );
    
        return $jwt;
    }

    这里使用了 Firebase 的 JWT 库(插件自带),JWT::encode() 函数负责将 Payload 编码成 JWT Token。

  • validate_token( $token ) 这个方法负责验证 JWT Token 的有效性。它会检查 Token 的签名、过期时间等。

    public function validate_token( $token ) {
        try {
            $token = JWT::decode( $token, $this->secret_key, array( 'HS256' ) );
            return $token;
        } catch ( Exception $e ) {
            return new WP_Error( 'jwt_auth_invalid_token', $e->getMessage(), array( 'status' => 403 ) );
        }
    }

    JWT::decode() 函数负责解码 JWT Token,并验证签名。如果验证失败,会抛出一个异常。

  • authenticate( $user ) 这个方法负责认证用户。它会在每次请求时检查 Authorization 头,如果存在有效的 JWT Token,就认证用户。

     public function authenticate( $user ) {
    
        $auth = isset( $_SERVER['HTTP_AUTHORIZATION'] ) ? $_SERVER['HTTP_AUTHORIZATION'] : false;
    
        if ( ! $auth ) {
            return $user;
        }
    
        list( $jwt ) = sscanf( $auth, 'Bearer %s' );
    
        if ( ! $jwt ) {
            return $user;
        }
    
        $token = $this->validate_token( $jwt );
    
        if ( is_wp_error( $token ) ) {
            return $token;
        }
    
        $user_id = isset( $token->data->user->id ) ? $token->data->user->id : false;
    
        if ( ! $user_id ) {
            return $user;
        }
    
        $user = get_user_by( 'ID', $user_id );
    
        return $user;
    }

    这个方法首先从 Authorization 头中提取 JWT Token,然后调用 validate_token() 方法验证 Token 的有效性。如果 Token 有效,就根据 Token 中的用户 ID 获取用户信息,并认证用户。

第七幕:自定义 JWT Payload

有时候,你可能需要在 JWT Payload 中添加自定义的数据,比如用户的角色、权限等等。你可以使用 jwt_auth_token_before_sign 过滤器来实现:

add_filter( 'jwt_auth_token_before_sign', 'my_custom_jwt_payload', 10, 2 );

function my_custom_jwt_payload( $payload, $user ) {
    $payload['data']['user']['roles'] = get_user_roles( $user->ID );
    return $payload;
}

这段代码会将用户的角色信息添加到 JWT Payload 中。

第八幕:JWT 的安全性考量

虽然 JWT 提供了无状态认证的便利,但也需要注意一些安全问题:

  • 密钥安全: 密钥是 JWT 的核心,务必妥善保管,不要泄露。
  • 过期时间: JWT 的过期时间不宜设置过长,避免 Token 被盗用。
  • Token 存储: 客户端需要安全地存储 JWT Token,防止被恶意获取。通常建议存储在 HttpOnly 的 Cookie 中,或者使用专门的安全存储方案。
  • 刷新 Token: 为了延长用户的会话时间,可以使用刷新 Token 机制。当 JWT Token 过期时,可以使用刷新 Token 来获取新的 JWT Token,而无需重新登录。

第九幕:代码示例:使用PHP获取wordpress文章

<?php

// WordPress 站点 URL
$wordpressUrl = 'https://your-wordpress-site.com'; // 替换成你的 WordPress 站点 URL
$apiEndpoint = $wordpressUrl . '/wp-json/wp/v2/posts';

// JWT Token (需要先通过登录获取)
$jwtToken = 'your_jwt_token'; // 替换成你的 JWT Token

// 设置 HTTP 请求头
$headers = [
    'Authorization: Bearer ' . $jwtToken,
    'Content-Type: application/json',
];

// 初始化 cURL 会话
$ch = curl_init($apiEndpoint);

// 设置 cURL 选项
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回响应内容而不是直接输出
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);   // 设置 HTTP 请求头
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 关闭 SSL 验证 (仅用于开发环境,生产环境请开启)

// 发送 GET 请求
$response = curl_exec($ch);

// 检查是否有错误发生
if (curl_errno($ch)) {
    echo 'cURL error: ' . curl_error($ch);
}

// 关闭 cURL 会话
curl_close($ch);

// 处理响应
if ($response) {
    $posts = json_decode($response, true); // 将 JSON 字符串解码成 PHP 数组

    if (is_array($posts)) {
        echo "<h2>WordPress 文章列表:</h2>";
        echo "<pre>";
        print_r($posts);
        echo "</pre>";
        // 遍历文章列表
      /*  foreach ($posts as $post) {
            echo '<h3>' . $post['title']['rendered'] . '</h3>';
            echo '<p>' . $post['excerpt']['rendered'] . '</p>';
            echo '<a href="' . $post['link'] . '">阅读更多</a><br><br>';
        }*/
    } else {
        echo '未能解析 JSON 响应: ' . $response;
    }
} else {
    echo '未收到任何响应。';
}

?>

请确保替换 your-wordpress-site.comyour_jwt_token 为你自己的 WordPress 站点 URL 和 JWT Token。 这个例子没有处理 refresh token,生产环境中需要考虑。

第十幕:总结与展望

今天我们深入探讨了 WordPress REST API 的 JWT 认证,包括 JWT 的原理、插件的使用、源码分析以及安全性考量。希望大家对 JWT 在 WordPress 中的应用有了更深入的理解。

JWT 作为一种轻量级的认证方案,在 RESTful API 中有着广泛的应用。 掌握 JWT 的使用,可以让你构建更安全、更高效的 WordPress 应用。

未来的 WordPress REST API 还有很多值得探索的地方,比如 GraphQL 支持、更细粒度的权限控制等等。让我们一起期待 WordPress 的发展,共同构建更美好的互联网世界!

感谢大家的观看,下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注