Glassfish环境下KeycloakOIDCFilter的过期令牌刷新与滑动过期需求
实现Keycloak AccessToken滑动过期的方案(Glassfish环境)
我明白你现在在Glassfish上用org.keycloak.adapters.servlet.KeycloakOIDCFilter做请求拦截和权限校验,想要给AccessToken加上滑动过期的功能对吧?这确实是个很实用的需求,能避免用户频繁被迫重新登录的麻烦。下面我给你梳理下可行的实现思路和具体步骤:
核心原理:利用Refresh Token实现滑动过期
AccessToken本身是JWT格式,过期时间(exp字段)是签发时就固定的,没法直接修改。滑动过期的核心逻辑是:当用户发起有效请求时,若AccessToken快过期了,就用Refresh Token向Keycloak服务器换取新的AccessToken,同时刷新会话的过期时间。
步骤1:先配置Keycloak后台支持刷新令牌
首先得确保你的Keycloak客户端和Realm配置正确,这是滑动过期的基础:
- 登录Keycloak后台,找到你的目标客户端:
- 把Access Type设置为
confidential(服务端应用推荐)或public(前后端分离场景); - 开启Refresh Token Enabled和Direct Access Grants Enabled;
- 在Advanced Settings里设置Refresh Token Lifespan,建议比你想要的滑动过期时间稍长(比如滑动过期设30分钟,这里就设35分钟)。
- 把Access Type设置为
- 进入Realm的Sessions设置页,配置Sso Session Idle Timeout,这个就是你想要的滑动过期时长(比如30分钟)——用户在这个时长内有活跃请求,会话就会自动延长。
步骤2:修改KeycloakOIDCFilter的校验逻辑
你当前的代码只是简单获取并验证AccessToken是否过期,要实现自动滑动过期,需要在每次请求时检查令牌剩余有效期,触发自动刷新:
@Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; // 从请求中获取Keycloak安全上下文 KeycloakSecurityContext kc = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); if (kc != null) { AccessToken accessToken = kc.getToken(); long currentTimestamp = System.currentTimeMillis() / 1000; long timeUntilExpiry = accessToken.getExp() - currentTimestamp; // 设置刷新阈值:比如剩余5分钟就触发刷新 long refreshThreshold = 5 * 60; if (timeUntilExpiry < refreshThreshold) { RefreshToken refreshToken = kc.getRefreshToken(); if (refreshToken == null) { // 没有Refresh Token,直接跳转到登录页 response.sendRedirect(request.getContextPath() + "/login"); return; } try { // 构建Keycloak客户端实例,调用刷新令牌接口 Keycloak keycloakClient = KeycloakBuilder.builder() .serverUrl("你的Keycloak服务器地址(比如http://localhost:8080/auth)") .realm("你的Realm名称") .clientId("你的客户端ID") .clientSecret("你的客户端秘钥(仅confidential类型需要)") .refreshToken(refreshToken.getToken()) .build(); // 执行令牌刷新 TokenResponse tokenResponse = keycloakClient.tokenManager().refreshToken(); // 解析新的令牌 AccessToken newAccessToken = RSATokenVerifier.create(tokenResponse.getToken()).getToken(); RefreshToken newRefreshToken = RefreshToken.fromTokenString(tokenResponse.getRefreshToken()); // 构建新的KeycloakSecurityContext并更新到请求和会话中 KeycloakSecurityContext newKcContext = new KeycloakSecurityContext( kc.getDeployment(), tokenResponse.getToken(), newAccessToken, tokenResponse.getRefreshToken(), newRefreshToken, kc.getTokenString() ); request.setAttribute(KeycloakSecurityContext.class.getName(), newKcContext); request.getSession().setAttribute(KeycloakSecurityContext.class.getName(), newKcContext); } catch (Exception e) { // 刷新失败(比如Refresh Token也过期了),引导用户重新登录 response.sendRedirect(request.getContextPath() + "/login"); return; } } } // 继续执行后续过滤器链 chain.doFilter(request, response); }
步骤3:注意事项
- 避免硬编码配置:把Keycloak服务器地址、Realm、客户端ID/秘钥这些参数放到Glassfish的配置文件(比如
web.xml或者自定义配置文件)里,方便后续修改。 - 异常处理要完善:令牌刷新可能失败(比如网络问题、Refresh Token过期),一定要做好跳转登录的兜底逻辑。
- 依赖检查:确保Glassfish项目里已经引入了Keycloak Servlet适配器的依赖,比如
keycloak-servlet-adapterjar包。 - 简化方案:如果你的Keycloak版本较新,其实可以不用自己写刷新逻辑——只要Realm和客户端的滑动过期配置正确,Keycloak的内置适配器会自动处理令牌刷新,你只需要确保应用正确保存了会话中的KeycloakSecurityContext即可。
内容的提问来源于stack exchange,提问作者user1026498
相关产品推荐
相关产品推荐

