SpringBoot+Shiro升级pac4j-cas与buji-pac4j后iframe和AJAX异常问题咨询
问题修复方案
问题1:iframe无法使用,返回X-Frame-Options deny错误
原因
pac4j 4.x+版本默认开启了安全响应头自动注入,其中X-Frame-Options默认值为DENY,禁止所有页面被iframe嵌套。
解决方法
修改pac4jConfig配置,调整X-Frame-Options规则:
@Bean public Config pac4jConfig() { // 你原有构建CasClient、Clients的代码保持不变 CasConfiguration casConfig = new CasConfiguration(); // 原有cas配置逻辑... CasClient casClient = new CasClient(casConfig); Clients clients = new Clients(shiroCasParams.getShiroServerUrlPrefix() + "/shiro-cas", casClient); Config config = new Config(clients); // 自定义安全响应头规则 DefaultSecurityHeaderLogic headerLogic = new DefaultSecurityHeaderLogic(); // 允许同域名下iframe嵌入(推荐) headerLogic.setXFrameOptions("SAMEORIGIN"); // 若需要允许指定域名嵌入,可使用如下配置(替换为实际允许的域名) // headerLogic.setContentSecurityPolicy("frame-ancestors 'self' https://允许的域名.com;"); // 测试环境可临时关闭X-Frame-Options(不推荐生产使用) // headerLogic.setXFrameOptions(null); config.setSecurityHeaderLogic(headerLogic); return config; }
问题2:AJAX请求被禁止
原因
有两个核心触发点:
- buji-pac4j 6.x默认开启CSRF校验,会自动拦截所有非GET请求校验CSRF令牌,和你自定义的
CsrfTokenFilter逻辑冲突 - 你现有的
CsrfTokenFilter没有处理浏览器跨域OPTIONS预检请求,导致AJAX跨域请求直接被拦截
解决方法
步骤1:关闭pac4j默认CSRF校验
修改ShiroConfig中shiroFilter方法里的SecurityFilter配置:
// cas 资源认证拦截器 SecurityFilter securityFilter = new CsrfTokenFilter(); securityFilter.setConfig(pac4jConfig); // 新增配置:关闭pac4j自带的CSRF校验,使用你自定义的token校验逻辑 securityFilter.setCsrfCheckEnabled(false); filters.put("securityFilter", securityFilter);
步骤2:修改CsrfTokenFilter兼容OPTIONS请求
调整tokenVerification方法和doFilter方法,支持跨域预检:
private boolean tokenVerification(ServletRequest request, ServletResponse response) { ShiroHttpServletRequest httpServletRequest = (ShiroHttpServletRequest) request; // GET、OPTIONS请求无需校验Token String method = httpServletRequest.getMethod(); if (StrUtil.equalsIgnoreCase(CommonConstant.HTTP_OPTIONS_GET, method) || StrUtil.equalsIgnoreCase("OPTIONS", method)) { return Boolean.TRUE; } // 原有token校验逻辑保持不变 String token = httpServletRequest.getHeader(HttpConstant.TOKEN_HEADER); if (StrUtil.isBlank(token)) { return Boolean.FALSE; } else { return ShiroUserUtil.isLogin() && token.equals(ShiroUserUtil.getToken()); } } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse resp = (HttpServletResponse) servletResponse; // 处理跨域OPTIONS预检请求 if ("OPTIONS".equalsIgnoreCase(req.getMethod())) { resp.setHeader("Access-Control-Allow-Origin", "*"); // 生产环境替换为实际允许的前端域名 resp.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); resp.setHeader("Access-Control-Allow-Headers", "Content-Type, " + HttpConstant.TOKEN_HEADER); resp.setHeader("Access-Control-Max-Age", "3600"); resp.setStatus(HttpServletResponse.SC_OK); return; } // 原有逻辑保持不变 if (!ShiroUserUtil.isSaveUserProfile() && StrUtil.isBlank(servletRequest.getParameter("appId"))) { resp.sendRedirect("http://xxxxxx/"); return; } if (tokenVerification(servletRequest, servletResponse) && monitorVerification()) { super.doFilter(servletRequest, servletResponse, filterChain); } }
内容的提问来源于stack exchange,提问作者OracleLin
相关产品推荐
相关产品推荐

