大数跨境
0
0

wordpress实现 JWT + Token黑名单

wordpress实现 JWT + Token黑名单 wordpress知识
2025-12-12
1

WordPress 原生 JWT 认证完整实现方案

本文提供一套无需第三方库的 WordPress 原生 JWT 认证解决方案,支持登录、刷新、解析、注销 Token,具备按用户角色自定义有效期、Token 黑名单机制及跨域处理功能。代码可直接嵌入主题 functions.php 文件使用。

/** * WordPress 纯原生JWT认证完整实现 * 功能:登录/刷新/解析/注销Token + 按角色自定义有效期 + 优化版黑名单 + 跨域处理 */// ===================== 核心配置 =====================// 务必替换为32位以上随机密钥(建议用https://generate-random.org/生成)define('WP_JWT_SECRET''your_32bit_random_secret_key_1234567890abcdef');// Token黑名单存储键名define('WP_JWT_BLACKLIST_KEY''wp_jwt_blacklist');// 默认时区(统一时间格式)date_default_timezone_set('Asia/Shanghai');// ===================== 角色-有效期映射(可自定义) =====================/** * 根据用户角色返回Token有效期(单位:秒) * @param array $user_roles 用户角色数组 * @return int 有效期(秒) */function wp_jwt_get_expire_by_role($user_roles{    $role_expire_map = [        'administrator' => 86400,  // 管理员:24小时        'editor'        => 43200,  // 编辑:12小时        'author'        => 14400,  // 作者:4小时        'subscriber'    => 7200,   // 订阅者:2小时        'contributor'   => 7200,   // 贡献者:2小时    ];    // 匹配优先级:从高权限到低权限    foreach ($role_expire_map as $role => $expire) {        if (in_array($role$user_roles)) {            return $expire;        }    }    return 7200// 兜底:2小时}// ===================== JWT核心工具函数 =====================/** * JWT专用Base64Url编码(区别于普通Base64) * @param string $data 待编码数据 * @return string 编码结果 */function wp_jwt_base64url_encode($data{    return rtrim(strtr(base64_encode($data), '+/''-_'), '=');}/** * JWT专用Base64Url解码 * @param string $data 待解码数据 * @return string 解码结果 */function wp_jwt_base64url_decode($data{    $padding strlen($data) % 4;    if ($padding 0) {        $data .= str_repeat('='4 - $padding);    }    return base64_decode(strtr($data'-_''+/'));}/** * 生成JWT Token(按角色动态有效期) * @param int $user_id WordPress用户ID * @return array Token数据(token + expire_seconds) */function wp_jwt_generate_token($user_id{    // 获取用户角色    $user get_user_by('ID'$user_id);    $user_roles $user->roles ?? ['subscriber'];    // 获取角色对应的有效期    $expire_seconds wp_jwt_get_expire_by_role($user_roles);
    // 1. 构造JWT Header(固定HS256算法)    $header json_encode([        'alg' => 'HS256',        'typ' => 'JWT'    ], JSON_UNESCAPED_UNICODE);
    // 2. 构造JWT Payload(包含用户信息+时间戳)    $payload json_encode([        'iss'   => get_site_url(),       // 签发者(站点域名)        'sub'   => $user_id,             // 主题(用户ID)        'iat'   => time(),               // 签发时间(Unix戳)        'exp'   => time() + $expire_seconds, // 过期时间(Unix戳)        'role'  => $user_roles[0] ?? 'subscriber' // 用户主角色    ], JSON_UNESCAPED_UNICODE);
    // 3. 生成签名(HMAC-SHA256)    $encoded_header wp_jwt_base64url_encode($header);    $encoded_payload wp_jwt_base64url_encode($payload);    $signature hash_hmac('sha256'$encoded_header '.' . $encoded_payload, WP_JWT_SECRET, true);    $encoded_signature wp_jwt_base64url_encode($signature);
    // 4. 拼接完整Token并返回    return [        'token' => $encoded_header '.' . $encoded_payload '.' . $encoded_signature,        'expire_seconds' => $expire_seconds    ];}/** * 验证JWT Token(含黑名单+签名+过期时间校验) * @param string $token 待验证的Token * @return array|false 验证通过返回用户数据,失败返回false */function wp_jwt_verify_token($token{    // 步骤1:检查是否在黑名单(优先级最高)    if (wp_jwt_check_blacklist($token)) {        return false;    }
    // 步骤2:校验Token格式(必须是3段式)    $token_parts explode('.'$token);    if (count($token_parts) !== 3) {        return false;    }    list($encoded_header$encoded_payload$encoded_signature) = $token_parts;
    // 步骤3:校验签名(防止Token被篡改)    $signature wp_jwt_base64url_decode($encoded_signature);    $expected_signature hash_hmac('sha256'$encoded_header '.' . $encoded_payload, WP_JWT_SECRET, true);    if (!hash_equals($expected_signature$signature)) { // 安全比较,防止时序攻击        return false;    }
    // 步骤4:解析Payload并校验过期时间    $payload json_decode(wp_jwt_base64url_decode($encoded_payload), true);    if (empty($payload['exp']) || $payload['exp'] < time()) {        return false;    }
    return [        'user_id' => $payload['sub'] ?? false,        'user_role' => $payload['role'] ?? 'subscriber',        'expire_at' => $payload['exp'] ?? 0    ];}// ===================== Token黑名单(优化版) =====================/** * 检查Token是否在黑名单中 * @param string $token 待检查的Token * @return bool true=在黑名单,false=不在 */function wp_jwt_check_blacklist($token{    $blacklist get_option(WP_JWT_BLACKLIST_KEY, []);    $token_hash hash('sha256'$token);    return isset($blacklist[$token_hash]) && $blacklist[$token_hash] > time();}/** * 将Token加入黑名单(自动清理过期数据) * @param string $token 待拉黑的Token * @return void */function wp_jwt_add_to_blacklist($token{    $blacklist get_option(WP_JWT_BLACKLIST_KEY, []);    $token_hash hash('sha256'$token);    $verify_data wp_jwt_verify_token($token);
    $blacklist[$token_hash] = $verify_data['expire_at'] ?? (time() + 7200);
    $now time();    $cleaned_blacklist array_filter($blacklist, function($expireuse ($now) {        return $expire > $now;    });
    update_option(WP_JWT_BLACKLIST_KEY, $cleaned_blacklist);}/** * 定时清理黑名单(每天执行一次) * @return void */function wp_jwt_clean_blacklist_cron() {    $blacklist get_option(WP_JWT_BLACKLIST_KEY, []);    $now time();    $cleaned_blacklist array_filter($blacklist, function($expireuse ($now) {        return $expire > $now;    });    update_option(WP_JWT_BLACKLIST_KEY, $cleaned_blacklist);}// 注册定时任务(仅激活一次)add_action('init', function() {    if (!wp_next_scheduled('wp_jwt_clean_blacklist')) {        wp_schedule_event(time(), 'daily''wp_jwt_clean_blacklist');    }});add_action('wp_jwt_clean_blacklist''wp_jwt_clean_blacklist_cron');// ===================== REST API接口注册 =====================add_action('rest_api_init', function() {    // 1. 登录接口:POST /wp-json/custom/v1/login    register_rest_route('custom/v1''/login', [        'methods' => 'POST',        'callback' => 'wp_jwt_login_handler',        'permission_callback' => '__return_true', // 登录接口无需权限    ]);
    // 2. 刷新Token接口:POST /wp-json/custom/v1/refresh-token    register_rest_route('custom/v1''/refresh-token', [        'methods' => 'POST',        'callback' => 'wp_jwt_refresh_handler',        'permission_callback' => '__return_true',    ]);
    // 3. 解析Token接口:POST /wp-json/custom/v1/parse-token(调试用)    register_rest_route('custom/v1''/parse-token', [        'methods' => 'POST',        'callback' => 'wp_jwt_parse_handler',        'permission_callback' => '__return_true',    ]);
    // 4. 注销Token接口:POST /wp-json/custom/v1/logout    register_rest_route('custom/v1''/logout', [        'methods' => 'POST',        'callback' => 'wp_jwt_logout_handler',        'permission_callback' => '__return_true',    ]);});// ===================== 接口回调函数 =====================/** * 登录接口回调:验证账号密码并生成Token * @param WP_REST_Request $request 请求对象 * @return WP_REST_Response|WP_Error */function wp_jwt_login_handler($request{    $username sanitize_text_field($request->get_param('username'));    $password sanitize_text_field($request->get_param('password'));
    $user wp_authenticate($username$password);    if (is_wp_error($user)) {        return new WP_Error('invalid_credentials','用户名或密码错误', ['status' => 401]);    }
    $token_data wp_jwt_generate_token($user->ID);    $expire_at time() + $token_data['expire_seconds'];
    return rest_ensure_response([        'code' => 200,        'message' => '登录成功',        'data' => [            'token' => $token_data['token'],            'user_id' => $user->ID,            'username' => $user->user_login,            'user_role' => $user->roles[0] ?? 'subscriber',            'expire_seconds' => $token_data['expire_seconds'],            'expire_at' => $expire_at,            'expire_at_readable' => date('Y-m-d H:i:s'$expire_at),            'issued_at' => time(),            'issued_at_readable' => date('Y-m-d H:i:s'time())        ]    ]);}/** * 刷新Token接口回调:旧Token换新版(旧Token立即拉黑) * @param WP_REST_Request $request 请求对象 * @return WP_REST_Response|WP_Error */function wp_jwt_refresh_handler($request{    $auth_header $request->get_header('Authorization') ?? '';    if (empty($auth_header) || !str_starts_with($auth_header'Bearer ')) {        return new WP_Error('missing_token','请在请求头中携带Bearer Token', ['status' => 401]);    }    $old_token substr($auth_header7);
    $verify_data wp_jwt_verify_token($old_token);    if (!$verify_data || !$verify_data['user_id']) {        return new WP_Error('invalid_token','旧Token无效或已过期', ['status' => 401]);    }
    wp_jwt_add_to_blacklist($old_token);
    $token_data wp_jwt_generate_token($verify_data['user_id']);    $expire_at time() + $token_data['expire_seconds'];
    return rest_ensure_response([        'code' => 200,        'message' => 'Token刷新成功',        'data' => [            'new_token' => $token_data['token'],            'user_id' => $verify_data['user_id'],            'user_role' => $verify_data['user_role'],            'expire_seconds' => $token_data['expire_seconds'],            'expire_at' => $expire_at,            'expire_at_readable' => date('Y-m-d H:i:s'$expire_at),            'issued_at' => time(),            'issued_at_readable' => date('Y-m-d H:i:s'time())        ]    ]);}/** * 解析Token接口回调:返回Token中的详细信息(调试用) * @param WP_REST_Request $request 请求对象 * @return WP_REST_Response|WP_Error */function wp_jwt_parse_handler($request{    $auth_header $request->get_header('Authorization') ?? '';    if (empty($auth_header) || !str_starts_with($auth_header'Bearer ')) {        return new WP_Error('missing_token','请在请求头中携带Bearer Token', ['status' => 401]);    }    $token substr($auth_header7);
    $token_parts explode('.'$token);    if (count($token_parts) !== 3) {        return new WP_Error('invalid_token_format','Token格式错误(必须是3段式)', ['status' => 400]);    }    $payload json_decode(wp_jwt_base64url_decode($token_parts[1]), true);
    $verify_data wp_jwt_verify_token($token);    $is_valid $verify_data true : false;
    return rest_ensure_response([        'code' => 200,        'data' => [            'is_valid' => $is_valid,            'payload' => [                'user_id' => $payload['sub'] ?? '',                'user_role' => $payload['role'] ?? '',                'issued_at' => $payload['iat'] ?? '',                'issued_at_readable' => !empty($payload['iat']) ? date('Y-m-d H:i:s'$payload['iat']) : '',                'expire_at' => $payload['exp'] ?? '',                'expire_at_readable' => !empty($payload['exp']) ? date('Y-m-d H:i:s'$payload['exp']) : '',                'is_expired' => !empty($payload['exp']) ? ($payload['exp'] < time()) : true,                'is_blacklisted' => wp_jwt_check_blacklist($token)            ]        ]    ]);}/** * 注销Token接口回调:将Token加入黑名单,实现退出登录 * @param WP_REST_Request $request 请求对象 * @return WP_REST_Response|WP_Error */function wp_jwt_logout_handler($request{    $auth_header $request->get_header('Authorization') ?? '';    if (empty($auth_header) || !str_starts_with($auth_header'Bearer ')) {        return new WP_Error('missing_token','请在请求头中携带Bearer Token', ['status' => 401]);    }    $token substr($auth_header7);
    wp_jwt_add_to_blacklist($token);
    return rest_ensure_response([        'code' => 200,        'message' => '注销成功,Token已失效',        'data' => []    ]);}// ===================== 全局Token验证(保护所有REST API) =====================add_filter('rest_authentication_errors', function($error) {    $current_route $_SERVER['REQUEST_URI'] ?? '';    $exclude_routes = [        '/wp-json/custom/v1/login',        '/wp-json/custom/v1/refresh-token',        '/wp-json/custom/v1/parse-token',        '/wp-json/custom/v1/logout'    ];    foreach ($exclude_routes as $route) {        if (strpos($current_route$route) !== false) {            return $error;        }    }
    $auth_header $_SERVER['HTTP_AUTHORIZATION'] ?? '';    if (empty($auth_header) || !str_starts_with($auth_header'Bearer ')) {        return new WP_Error('missing_token','访问该接口需要携带有效的JWT Token', ['status' => 401]);    }    $token substr($auth_header7);
    $verify_data wp_jwt_verify_token($token);    if (!$verify_data || !$verify_data['user_id']) {        return new WP_Error('invalid_token','Token无效、已过期或已被注销', ['status' => 401]);    }
    wp_set_current_user($verify_data['user_id']);
    return $error;});// ===================== 跨域处理(前端请求必备) =====================add_action('rest_pre_serve_request', function() {    $allowed_origin $_SERVER['HTTP_ORIGIN'] ?? '*';    header("Access-Control-Allow-Origin: {$allowed_origin}");    header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");    header("Access-Control-Allow-Headers: Authorization, Content-Type, X-Requested-With");    header("Access-Control-Allow-Credentials: true");
    if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {        status_header(200);        exit();    }});

核心功能说明

接口路径

请求方法

功能

备注

/wp-json/custom/v1/login

POST

账号密码登录,生成 Token

参数:username/password

/wp-json/custom/v1/refresh-token

POST

刷新 Token(旧 Token 拉黑)

请求头:Authorization: Bearer 旧Token

/wp-json/custom/v1/parse-token

POST

解析 Token 信息(调试)

请求头:Authorization: Bearer Token

/wp-json/custom/v1/logout

POST

注销 Token(加入黑名单)

请求头:Authorization: Bearer Token

使用注意事项

  • 密钥替换:务必更换 WP_JWT_SECRET 为 32 位以上随机字符串,避免安全风险;

  • 跨域配置:生产环境应将 Access-Control-Allow-Origin 由 * 改为具体前端域名;

  • 角色配置:可通过 wp_jwt_get_expire_by_role 函数自定义各角色 Token 有效期;

  • HTTPS 强制:生产环境需启用 HTTPS,防止 Token 被窃取。

【声明】内容源于网络
0
0
wordpress知识
各类跨境出海行业相关资讯
内容 265
粉丝 0
wordpress知识 各类跨境出海行业相关资讯
总阅读1.4k
粉丝0
内容265