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

如何解决Session认证前端调用JWT保护API的Token过期问题?

解决Vert.x + Keycloak跨应用认证的令牌刷新问题

核心问题原因

应用1的OAuth2Auth Session默认不会自动刷新访问令牌,当初始令牌过期后,会话中存储的principal仍保留旧令牌,导致前端获取的令牌无效,调用API时返回401。

调整方案

1. 配置OAuth2Auth自动处理令牌刷新

初始化OAuth2Auth时,开启刷新令牌支持并添加必要权限,让Vert.x能自动管理令牌生命周期:

OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.AUTH_CODE, new OAuth2Options()
  .setClientId("your-client-id")
  .setClientSecret("your-client-secret")
  .setSite("https://your-keycloak-domain/auth/realms/your-realm")
  .setRefreshToken(true) // 启用刷新令牌支持
  .setTokenPath("/protocol/openid-connect/token")
  .setAuthorizationPath("/protocol/openid-connect/auth")
  .setScope("openid offline_access")); // 添加offline_access获取持久化刷新令牌

同时确保会话存储能保留刷新令牌,避免应用重启后丢失。

2. 修改应用1的令牌端点逻辑,主动检查并刷新令牌

在提供访问令牌的端点中,先判断令牌是否过期或即将过期,触发刷新并更新会话:

router.get("/api/token").handler(ctx -> {
  User user = ctx.user();
  if (user == null) {
    ctx.response().setStatusCode(401).end("Unauthorized");
    return;
  }

  JsonObject principal = user.principal();
  String accessToken = principal.getString("access_token");
  JsonObject decodedJwt = JWT.decode(accessToken);
  long expiresAt = decodedJwt.getLong("exp") * 1000;

  // 提前30秒触发刷新,避免令牌刚好用过期
  if (System.currentTimeMillis() >= expiresAt - 30000) {
    OAuth2Auth oauth2 = ctx.vertx().getLocal("oauth2-auth");
    oauth2.refresh(user)
      .onSuccess(refreshedUser -> {
        ctx.setUser(refreshedUser);
        ctx.response()
          .putHeader("Content-Type", "application/json")
          .end(new JsonObject().put("access_token", refreshedUser.principal().getString("access_token")).encode());
      })
      .onFailure(err -> {
        // 刷新失败,引导用户重新登录
        ctx.response().setStatusCode(401).end("Token refresh failed, please re-login");
      });
  } else {
    // 令牌有效,直接返回
    ctx.response()
      .putHeader("Content-Type", "application/json")
      .end(new JsonObject().put("access_token", accessToken).encode());
  }
});

3. 前端配合优化令牌生命周期管理

前端存储令牌时同步保存过期时间戳:

  • 每次调用API前,检查当前时间是否接近过期(比如提前1分钟)
  • 若即将过期,先调用应用1的令牌端点获取新令牌,再发起API请求
  • 若刷新失败,直接引导用户重新登录

4. 采用持久化会话存储

替换默认内存会话存储为Redis等分布式存储,确保多实例部署或应用重启时,刷新令牌不会丢失:

RedisOptions redisOptions = new RedisOptions().setHost("your-redis-host");
Redis redis = Redis.createClient(vertx, redisOptions);
SessionStore sessionStore = RedisSessionStore.create(vertx, redis);

Router router = Router.router(vertx);
router.route().handler(SessionHandler.create(sessionStore));

内容的提问来源于stack exchange,提问作者Lukas Weber

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 21:30:13