DataWizardess 2026-02-28 12:20 采纳率: 99.1%
浏览 1
已采纳

若依前后端分离项目集成CAS 5.3时,如何解决前端跨域获取Ticket Granting Ticket(TGT)失败?

在若依前后端分离项目集成 CAS 5.3 时,前端(Vue + axios)直接向 CAS `/login` 端点发起 POST 请求以获取 TGT,常因浏览器同源策略触发跨域(CORS)拦截,导致 403/0 status 或预检失败;根本原因在于 CAS 5.3 默认未启用 CORS 支持,且 TGT 创建依赖 JSESSIONID Cookie 的 Secure+HttpOnly 会话,而前端跨域请求默认不携带凭证(`withCredentials: false`),致使 CAS 无法关联会话、拒绝颁发 TGT。此外,CAS 登录成功后重定向至前端回调页时,若未正确配置 `cas.service` 参数或前端未处理 `ticket` 查询参数,亦会导致认证链断裂。该问题并非单纯代理配置可解,需协同服务端 CORS 策略、会话 Cookie 属性、前端凭证传递及 CAS 客户端逻辑三端调整。
  • 写回答

1条回答 默认 最新

  • 爱宝妈 2026-02-28 12:20
    关注
    ```html

    一、现象层:前端跨域请求被拦截的典型表现

    • Vue 调用 axios.post('https://cas.example.com/cas/login', formData) 返回 status=0403 Forbidden
    • 浏览器控制台报错:Blocked by CORS policy: No 'Access-Control-Allow-Origin' header
    • 预检请求(OPTIONS)失败,响应头缺失 Access-Control-Allow-Credentials: true
    • 登录成功后重定向至 https://front.example.com/callback?ticket=ST-xxx,但前端未捕获或校验 ticket,导致认证中断;
    • Network 面板可见 JSESSIONID Cookie 未随请求发送(withCredentials=false 默认行为)。

    二、机制层:CAS 5.3 认证会话与 CORS 的底层耦合关系

    CAS 5.3 的 TGT 创建强依赖容器级 HTTP 会话(JSESSIONID),该 Cookie 默认标记为 Secure; HttpOnly; SameSite=Lax。而跨域 POST 请求若未显式启用凭证传递,则:

    • 浏览器拒绝发送 HttpOnly Cookie;
    • CAS Server 无法关联已有会话,强制新建会话并拒绝 TGT 颁发;
    • 即使手动设置 withCredentials: true,服务端若未返回 Access-Control-Allow-Credentials: true,浏览器仍拦截响应。

    三、配置层:CAS 5.3 服务端 CORS 与 Cookie 策略改造

    需在 CAS Overlay 项目中修改 cas-server-support-rest-authentication 模块,并注入自定义 CORS 过滤器:

    // src/main/java/org/apereo/cas/config/CorsConfiguration.java
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("https://front.example.com");
        config.applyPermitDefaultValues();
        source.registerCorsConfiguration("/cas/login", config);
        source.registerCorsConfiguration("/cas/serviceValidate", config);
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new CorsConfigurationFilter(source));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }

    四、协议层:CAS 认证流程与 service 参数的语义一致性

    关键参数必须全程一致且 URL 编码安全:

    环节参数名取值要求验证要点
    前端发起登录servicehttps%3A%2F%2Ffront.example.com%2Fcallback必须与 CAS 登录页重定向目标完全匹配
    CAS 重定向回调ticketST-开头的 Service Ticket前端须提取并透传至后端验证接口

    五、实现层:若依前端(Vue + axios)的完整 CAS 客户端逻辑

    需封装三层职责:凭证携带、ticket 提取、ST 校验委托:

    // api/cas.js
    export function casLogin(username, password) {
      const formData = new URLSearchParams();
      formData.append('username', username);
      formData.append('password', password);
      formData.append('service', encodeURIComponent('https://front.example.com/callback'));
      
      return axios.post('https://cas.example.com/cas/login', formData, {
        withCredentials: true, // ⚠️ 必须开启
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
      });
    }
    
    // router/index.js 中监听 callback 路由
    router.beforeEach((to, from, next) => {
      if (to.path === '/callback' && to.query.ticket) {
        validateServiceTicket(to.query.ticket).then(res => {
          store.dispatch('user/loginByCas', res.data); // 写入若依 token
          next({ path: '/' });
        });
      } else next();
    });

    六、验证层:全链路调试检查清单(Checklist)

    • ✅ CAS Server 响应头含:Access-Control-Allow-Origin: https://front.example.com & Access-Control-Allow-Credentials: true
    • ✅ 浏览器 Application → Cookies 中 JSESSIONID 属性为 Secure; HttpOnly; SameSite=None(HTTPS 环境下);
    • ✅ 若依后端接收 ST 后调用 https://cas.example.com/cas/serviceValidate?service=...&ticket=... 成功返回 <cas:authenticationSuccess>
    • ✅ 前端 axios 请求 Network 面板显示 Request Headers 含 Cookie: JSESSIONID=xxx
    • ✅ CAS 日志(cas.log)出现 TicketGrantingTicketImpl createdServiceTicketImpl granted

    七、架构层:为何反向代理无法替代 CORS+Credential 协同方案?

    若仅通过 Nginx 将 /cas/ 代理至 CAS Server,虽规避前端跨域,但引入新问题:

    • 代理后 CAS 重定向 Location 头仍指向原始 CAS 域名(非前端域名),导致浏览器跳转出当前站点;
    • CAS 内置的 ServiceProperties 校验逻辑基于原始 service 参数,代理层无法篡改重定向 URL 中的 Host;
    • Cookie 的 Domain 属性若设为 .example.com,则代理与 CAS 同域时可共享,但违背微服务隔离原则,且无法解决开发环境多端口调试问题。

    八、演进层:CAS 6.x+ 与 Spring Security OAuth2 的融合趋势

    面向未来架构升级,建议预留扩展点:

    • 将 CAS Client 抽离为独立认证网关(如 Spring Cloud Gateway + CAS REST API);
    • 若依后端集成 spring-security-cas 替代手写 ST 验证,利用 CasAuthenticationFilter 自动完成票据解析与 Principal 构建;
    • 前端采用 PKCE 流程对接 CAS OAuth2 扩展模块(cas-server-support-oauth-webflow),规避 Cookie 依赖,适配移动端与第三方 SPA。

    九、可视化层:CAS 与若依集成认证流程(Mermaid)

    graph LR A[Vue 前端] -->|1. POST /cas/login
    withCredentials=true| B(CAS Server) B -->|2. Set-Cookie: JSESSIONID
    Access-Control-Allow-*| A B -->|3. 302 Redirect
    Location: service?ticket=ST-xxx| C[前端 callback 路由] C -->|4. 提取 ticket| D[若依后端] D -->|5. GET /cas/serviceValidate?service=...&ticket=...| B B -->|6. XML 响应
    <cas:authenticationSuccess>| D D -->|7. 返回若依 JWT Token| A

    十、生产层:安全加固与灰度发布关键项

    • 禁用 CAS Debug 模式(cas.debug.enabled=false)防止敏感信息泄露;
    • JSESSIONID Cookie 必须设置 SameSite=None; Secure(Chrome 80+ 强制要求);
    • 前端 ticket 获取后立即单次使用,禁止缓存或重复提交;
    • 若依后端校验 ST 时需同步校验 renew=false 参数,防止会话固定攻击;
    • 灰度阶段在 CAS Server 添加 cas.authn.accept.users 白名单,限制首批接入用户范围。
    ```
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 3月1日
  • 创建了问题 2月28日