如何解决Quarkus OIDC Cookie过期与Ajax请求CORS失败问题?
环境与配置
基于Quarkus + Primefaces(服务端渲染)的Web应用,已集成Quarkus OIDC,核心配置如下:
quarkus.oidc.auth-server-url=${oidc_serverUrl} quarkus.oidc.client-id=my-jsf-app quarkus.oidc.application-type=web-app quarkus.oidc.credentials.secret=${oidc_secret} quarkus.http.auth.permission.authenticated.paths=/secure/* quarkus.http.auth.permission.authenticated.policy=authenticated quarkus.oidc.authentication.cookie-same-site=lax quarkus.oidc.tls.verification=none # 仅认证用户可发起登出 quarkus.oidc.logout.path=/secure/logout # 登出后跳转至首页 quarkus.oidc.logout.post-logout-path=/secure/user/home.xhtml quarkus.oidc.token-state-manager.split-tokens=true quarkus.oidc.token-state-manager.strategy=id-refresh-tokens quarkus.oidc.authentication.cookie-path=/secure/
问题现象
当浏览器端的q_session和q_session_rt Cookie过期后,首个Ajax请求会被重定向至OIDC端点进行重新认证,触发CORS报错:
Access to XMLHttpRequest at 'https://keycloak.myapp.com/auth/realms/my-app/protocol/openid-connect/auth?response_type=code&client_id=my-jsf-app&scope=openid&redirect_uri=https%3A%2F%2Fdev.myapp.com%2Fsecure%2Fuser%2FaccountWizard.xhtml&state=b8afe3c2-0302-4592-a2b4-ad67997fad32' (redirected from 'https://dev.myapp.com/secure/user/accountWizard.xhtml?jfwid=cc9f2') from origin 'https://dev.myapp.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Ajax请求失败后,所有常规GET/POST请求返回401错误,强制页面刷新也无法正常工作,用户需清除浏览器缓存才能恢复。
日志信息
Quarkus应用日志输出:
2023-06-10 21:49:22,257 DEBUG [io.qua.oid.run.CodeAuthenticationMechanism] (vert.x-eventloop-thread-1) State parameter can not be empty or multi-valued if the state cookie is present
解决方案
1. 拦截Ajax未授权响应,触发页面跳转
Primefaces的Ajax请求无法处理跨域重定向,需通过全局Ajax错误处理器拦截401状态码,手动清除无效Cookie并跳转到登录页面:
PrimeFaces.settings.ajax.error = function(xhr, status, error) { if (xhr.status === 401) { // 清除过期的认证Cookie document.cookie = "q_session=; path=/secure/; expires=Thu, 01 Jan 1970 00:00:00 UTC"; document.cookie = "q_session_rt=; path=/secure/; expires=Thu, 01 Jan 1970 00:00:00 UTC"; // 跳转到应用首页触发重新认证 window.location.href = '/secure/user/home.xhtml'; } };
2. 调整Quarkus OIDC配置解决状态参数冲突
日志中的状态参数错误由无效Cookie残留导致,可调整Token状态管理配置:
- 禁用拆分Token配置,避免状态Cookie与请求参数冲突:
quarkus.oidc.token-state-manager.split-tokens=false
- 扩展认证Cookie路径,确保所有受保护路径都能携带Cookie:
quarkus.oidc.authentication.cookie-path=/
3. 验证Keycloak Web Origins配置
确认Keycloak客户端的Web Origins已添加完整的应用域名(如https://dev.myapp.com),不要添加路径后缀,确保CORS策略覆盖整个域名的请求。
4. 后端拦截Ajax重定向,返回401状态码
添加Quarkus响应过滤器,针对Ajax请求直接返回401而非重定向,让前端统一处理跳转逻辑:
import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.Provider; @Provider public class AjaxAuthResponseFilter implements ContainerResponseFilter { @Override public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) { String ajaxHeader = requestContext.getHeaderString("X-Requested-With"); if ("XMLHttpRequest".equals(ajaxHeader) && responseContext.getStatus() == 302) { responseContext.setStatus(401); responseContext.getHeaders().putSingle("Content-Type", MediaType.TEXT_PLAIN); responseContext.setEntity("Unauthorized"); } } }
内容的提问来源于stack exchange,提问作者zeppelinux

