Vert.x自定义认证抛出Response head already sent异常问题求助
Vert.x Mutiny 无JWT认证端点实现方案
问题根源分析
java.lang.IllegalStateException: Response head already sent异常本质是重复发送响应(比如同时调用response.end()和ctx.next()),或是在响应已发送后尝试修改响应、触发后续处理器。- 移除
ctx.response()和ctx.next()后返回204,是因为未主动结束响应,Vert.x默认返回空内容的204状态码。
正确的Login端点实现(Mutiny风格)
基于你自定义的CustomAuthenticationProvider,用Mutiny的Uni链式调用处理,避免回调混用导致的响应发送冲突:
// 注入自定义认证提供者 @Inject CustomAuthenticationProvider authProvider; // 配置Login路由 router.post("/login").handler(ctx -> { // 解析JSON请求体,提取手机号和密码 ctx.body().asJsonObject() .onItem().transform(body -> { String phone = body.getString("phone"); String password = body.getString("password"); // 构建认证凭证 return Credentials.create() .put("phone", phone) .put("password", password); }) .onItem().transformToUni(credentials -> authProvider.authenticate(credentials)) .subscribe().with( user -> { // 认证成功:将用户信息存入会话 ctx.session().put("user", user.principal()); // 发送成功响应 ctx.response() .setStatusCode(200) .putHeader("Content-Type", "application/json") .end("{\"status\":\"success\",\"message\":\"登录成功\"}"); }, error -> { // 认证失败:发送错误响应 ctx.response() .setStatusCode(401) .putHeader("Content-Type", "application/json") .end("{\"status\":\"error\",\"message\":\"手机号或密码错误\"}"); } ); });
注意:登录端点是终结点,不要调用ctx.next(),所有响应操作都在Uni的回调里完成,确保响应只发送一次。
无JWT的端点保护方案(基于Vert.x Session)
用Vert.x内置的Session机制实现会话认证,无需依赖JWT:
1. 启用Session支持
启动Vert.x时配置Session存储(开发环境用内存存储,生产环境可替换为Redis等分布式存储):
Vertx vertx = Vertx.vertx(); Router router = Router.router(vertx); // 启用本地Session存储 SessionStore sessionStore = LocalSessionStore.create(vertx); router.route().handler(SessionHandler.create(sessionStore));
2. 编写认证拦截器
创建全局拦截器,校验会话中的用户信息,未登录则拦截请求:
// 拦截所有/api前缀的请求 router.route("/api/*").handler(ctx -> { JsonObject user = ctx.session().get("user"); if (user == null) { // 未登录,返回401 ctx.response() .setStatusCode(401) .putHeader("Content-Type", "application/json") .end("{\"status\":\"error\",\"message\":\"请先登录\"}"); } else { // 已登录,放行至后续处理器 ctx.next(); } });
3. 受保护端点示例
router.get("/api/user/profile").handler(ctx -> { JsonObject user = ctx.session().get("user"); ctx.response() .setStatusCode(200) .putHeader("Content-Type", "application/json") .end(user.encode()); });
关键注意事项
- 避免Mutiny Uni与Vert.x回调API混用:不要在Uni回调中同时调用
response.end()和ctx.next(),会导致响应重复发送。 - 会话存储选型:内存存储仅适合开发环境,生产环境推荐使用
RedisSessionStore实现分布式会话。 - 登出功能实现:直接销毁会话即可:
router.post("/logout").handler(ctx -> { ctx.session().destroy(); ctx.response() .setStatusCode(200) .putHeader("Content-Type", "application/json") .end("{\"status\":\"success\",\"message\":\"已登出\"}"); });
内容的提问来源于stack exchange,提问作者Plus Ultra
相关产品推荐
相关产品推荐

