You need to enable JavaScript to run this app. SpringBoot+Shiro升级pac4j-cas与buji-pac4j后iframe和AJAX异常问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

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请求被禁止

原因

有两个核心触发点:

  1. buji-pac4j 6.x默认开启CSRF校验,会自动拦截所有非GET请求校验CSRF令牌,和你自定义的CsrfTokenFilter逻辑冲突
  2. 你现有的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 00:57:04