You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CodeIgniter 4 REST API认证过滤器异常:篡改JWT令牌后无法返回自定义响应

解决CodeIgniter 4认证过滤器篡改令牌返回500错误的问题

问题回顾

你在CI4开发REST API的认证过滤器时遇到了一个头疼的问题:令牌有效或者没传令牌时都正常,但篡改令牌内容后,本该返回自定义的401响应,结果却出现了500错误,控制台里满是JSON解析失败的报错。

你遇到的错误日志(简化后):

Fatal error: Uncaught CodeIgniter\Format\Exceptions\FormatException: Failed to parse json string, error: "Malformed UTF-8 characters, possibly incorrectly encoded".

你当前使用的app/Filters/Auth.php代码:

<?php namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class Auth implements FilterInterface {
    public function before(RequestInterface $request, $arguments = null) {
        $key = getenv('JWT_SECRET');
        $header = $request->getServer('HTTP_AUTHORIZATION');
        $token = null;

        if (!empty($header)) {
            $token = explode(' ', $header)[1];
        }

        if (is_null($token) || empty($token)) {
            $response = [
                'status' => 401,
                'error' => true,
                'message' => 'Token required.',
                'data' => []
            ];
            echo json_encode($response);
            exit();
        }

        try {
            JWT::decode($token, new Key($key, 'HS256'));
        } catch (Exception $ex) {
            $response = [
                'status' => 401,
                'error' => true,
                'message' => 'Access denied.',
                'data' => []
            ];
            echo json_encode($response);
            exit();
        }
    }

    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) {
        //
    }
}

问题根源

我帮你排查了下,问题主要出在两个地方:

  1. 异常捕获不全:你只捕获了Exception,但Firebase JWT库抛出的是DomainException、UnexpectedValueException这类更具体的异常,而且你没引入对应的命名空间,导致这些异常没被捕获,触发了CI4的全局异常处理。
  2. 响应方式不规范:你直接用echo json_encode()加exit()的方式返回结果,这种写法绕过了CI4的响应处理流程,当有未捕获的异常时,系统会尝试格式化响应,进而引发UTF-8编码错误。

修复方案

下面是修正后的代码,我标注了关键修改点:

<?php namespace App\Filters;

use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
// 1. 引入JWT所有可能抛出的异常类
use Firebase\JWT\ExpiredException;
use Firebase\JWT\InvalidArgumentException;
use DomainException;
use UnexpectedValueException;

class Auth implements FilterInterface {
    public function before(RequestInterface $request, $arguments = null) {
        $key = getenv('JWT_SECRET');
        $header = $request->getServer('HTTP_AUTHORIZATION');
        $token = null;

        if (!empty($header)) {
            // 额外加个判断,确保Authorization头格式是Bearer xxxx
            $parts = explode(' ', $header);
            if (count($parts) === 2 && strtolower($parts[0]) === 'bearer') {
                $token = $parts[1];
            } else {
                return $this->sendUnauthorized('Invalid authorization header format.');
            }
        }

        if (is_null($token) || empty($token)) {
            return $this->sendUnauthorized('Token required.');
        }

        try {
            JWT::decode($token, new Key($key, 'HS256'));
        } catch (ExpiredException $ex) {
            // 单独捕获令牌过期的情况,返回更友好的提示
            return $this->sendUnauthorized('Token has expired.');
        } catch (InvalidArgumentException | DomainException | UnexpectedValueException $ex) {
            // 2. 捕获所有JWT解码失败的异常
            return $this->sendUnauthorized('Access denied.');
        }
    }

    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) {
        // 无需修改
    }

    /**
     * 3. 封装统一的401响应方法,符合CI4规范
     * @param string $message
     * @return ResponseInterface
     */
    private function sendUnauthorized(string $message): ResponseInterface {
        $response = service('response');
        $response->setStatusCode(ResponseInterface::UNAUTHORIZED);
        $response->setJSON([
            'status' => 401,
            'error' => true,
            'message' => $message,
            'data' => []
        ]);
        return $response;
    }
}

修改说明

  • 捕获全部异常:把Firebase JWT可能抛出的所有异常都引入并捕获,确保不会有未处理的异常漏出去触发全局错误。
  • 规范响应方式:用CI4官方的service('response')来构建响应,设置状态码和JSON内容后返回,完全遵循CI4的请求处理流程,不会再出现编码问题。
  • 封装响应逻辑:把重复的401响应代码封装成私有方法,后续修改响应格式只需要改这一处,更便于维护。

这样修改后,再去篡改令牌测试,就能得到你想要的自定义401响应,不会再出现500错误了。

内容的提问来源于stack exchange,提问作者Lidia K.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 22:34:11