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($expire) use ($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($expire) use ($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/loginregister_rest_route('custom/v1', '/login', ['methods' => 'POST','callback' => 'wp_jwt_login_handler','permission_callback' => '__return_true', // 登录接口无需权限]);// 2. 刷新Token接口:POST /wp-json/custom/v1/refresh-tokenregister_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/logoutregister_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_header, 7);$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_header, 7);$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_header, 7);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_header, 7);$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 被窃取。

