使用Silex JWT认证时如何记录请求并返回自定义无效令牌提示
解决Silex+JWT中请求日志记录与自定义错误提示的问题
我之前在使用Silex结合JWT做API认证时,也碰到过和你一模一样的问题——未携带令牌的请求直接返回默认错误,自己写的before中间件根本没触发,日志也打不上。后来才搞明白,核心问题出在中间件执行顺序和JWT认证事件的处理上,下面给你详细说解决方案:
一、为什么$app->before()没触发?
默认情况下,Lexik JWT认证的中间件优先级比你自定义的before中间件高(Silex中间件默认优先级是0,数字越大越先执行)。当请求没有携带令牌时,JWT中间件会在你的before之前就拦截请求,直接返回错误响应,导致你的日志代码根本没机会执行。
解决方法:提升日志中间件的优先级
把你的日志before中间件优先级设为一个较高的数值(比如100),确保它在JWT认证中间件之前执行:
use Symfony\Component\HttpFoundation\Request; // 记录所有请求的日志,优先级100确保最先执行 $app->before(function (Request $request) use ($app) { $tokenExists = $request->headers->has('Authorization') && str_starts_with($request->headers->get('Authorization'), 'Bearer '); $app['logger']->info('API 请求记录', [ '请求路径' => $request->getPathInfo(), '请求方法' => $request->getMethod(), '是否携带令牌' => $tokenExists ? '是' : '否', '客户端IP' => $request->getClientIp() ]); }, 100);
二、自定义令牌无效/缺失的错误提示
JWT认证组件提供了专门的事件来处理认证失败的场景,我们只需要监听这些事件,替换默认的响应内容即可:
1. 监听「令牌缺失」事件
当请求完全没有携带JWT令牌时,会触发lexik_jwt_authentication.on_missing_token事件:
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTMissingTokenEvent; use Symfony\Component\HttpFoundation\JsonResponse; $app['dispatcher']->addListener('lexik_jwt_authentication.on_missing_token', function (JWTMissingTokenEvent $event) { $customResponse = new JsonResponse([ 'code' => 401, 'message' => '请先登录获取有效的身份令牌,再访问此接口' ], 401); $event->setResponse($customResponse); });
2. 监听「令牌无效/认证失败」事件
当令牌过期、签名错误等无效情况时,会触发lexik_jwt_authentication.on_authentication_failure事件:
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTAuthenticationFailureEvent; $app['dispatcher']->addListener('lexik_jwt_authentication.on_authentication_failure', function (JWTAuthenticationFailureEvent $event) { $customResponse = new JsonResponse([ 'code' => 401, 'message' => '身份令牌无效,请重新登录' ], 401); $event->setResponse($customResponse); });
三、完整的整合示例
把上面的代码和你原有的接口代码整合起来,大概是这样:
use Silex\Application; use Symfony\Component\HttpFoundation\Request; use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTMissingTokenEvent; use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTAuthenticationFailureEvent; use Symfony\Component\HttpFoundation\JsonResponse; $app = new Application(); // 1. 配置日志服务(假设你已经配置好了monolog等日志组件) $app['monolog.logfile'] = __DIR__.'/logs/api.log'; $app->register(new Silex\Provider\MonologServiceProvider(), [ 'monolog.name' => 'api', 'monolog.logfile' => $app['monolog.logfile'], ]); // 2. 注册JWT认证服务(你的原有配置) // $app->register(new Lexik\Bundle\JWTAuthenticationBundle\Provider\JWTServiceProvider(), [ // 'lexik_jwt_authentication.secret_key' => 'your_secret_key', // // 其他JWT配置... // ]); // 3. 高优先级的请求日志中间件 $app->before(function (Request $request) use ($app) { $tokenExists = $request->headers->has('Authorization') && str_starts_with($request->headers->get('Authorization'), 'Bearer '); $app['logger']->info('API 请求记录', [ '请求路径' => $request->getPathInfo(), '请求方法' => $request->getMethod(), '是否携带令牌' => $tokenExists ? '是' : '否', '客户端IP' => $request->getClientIp() ]); }, 100); // 4. 自定义令牌缺失响应 $app['dispatcher']->addListener('lexik_jwt_authentication.on_missing_token', function (JWTMissingTokenEvent $event) { $customResponse = new JsonResponse([ 'code' => 401, 'message' => '请先登录获取有效的身份令牌,再访问此接口' ], 401); $event->setResponse($customResponse); }); // 5. 自定义令牌无效响应 $app['dispatcher']->addListener('lexik_jwt_authentication.on_authentication_failure', function (JWTAuthenticationFailureEvent $event) { $customResponse = new JsonResponse([ 'code' => 401, 'message' => '身份令牌无效,请重新登录' ], 401); $event->setResponse($customResponse); }); // 6. 你的受保护接口 $app->get('/api/protected_resource', function() use ($app){ // 接口逻辑 return new JsonResponse(['data' => '这是受保护的资源']); })->before($app['lexik_jwt_authentication.authentication_handler']); $app->run();
这样调整后,不管请求是否携带令牌,都会先触发日志中间件记录请求;同时令牌缺失或无效时,会返回你自定义的提示信息,而不是默认的错误内容。
内容的提问来源于stack exchange,提问作者Divesh Oswal
相关产品推荐
相关产品推荐

