package com.yunhai.userbase.config; import com.yunhai.platform.auth.spring.YhSsoProperties; import org.jasig.cas.client.authentication.AuthenticationFilter; import org.jasig.cas.client.session.SingleSignOutFilter; import org.jasig.cas.client.util.HttpServletRequestWrapperFilter; import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 浏览器端 CAS 单点登录过滤器(仅作用于 {@code /api/*})。

* 基础平台 yh-auth 只负责「读 CAS 登录态 + 补全用户明细」,并不提供浏览器重定向; * 本配置补齐标准 CAS 客户端三件套:
* 1) {@link SingleSignOutFilter} 单点登出;
* 2) {@link AuthenticationFilter} 未登录时重定向到 CAS 登录页;
* 3) {@link Cas20ProxyReceivingTicketValidationFilter} 校验 service ticket 并建立会话;
* 4) {@link HttpServletRequestWrapperFilter} 让 {@code request.getRemoteUser()} 可用(yh-auth 的 * {@code CasSessionAuthStrategy} 依赖它)。

* 顺序须早于 yh-app-auth 的 {@code AppAuthFilter}(HIGHEST_PRECEDENCE+10), * 且只对 {@code /api/*} 生效;{@code /oapi}、{@code /open/api} 走签名验签,不在此列。 */ @Configuration @ConditionalOnProperty(prefix = "yh.sso", name = "server-url-prefix") public class CasSsoConfiguration { @Autowired private YhSsoProperties ssoProperties; /** 从 client-host-url 抽取 scheme://host[:port](去掉上下文路径),供给 CAS 构造 service 参数。 */ private String serverName() { String client = ssoProperties.getClientHostUrl(); if (client == null || client.trim().isEmpty()) { return null; } Matcher m = Pattern.compile("^(https?://[^/]+)").matcher(client.trim()); return m.find() ? m.group(1) : client.trim(); } @Bean public FilterRegistrationBean casSingleSignOutFilter() { FilterRegistrationBean bean = new FilterRegistrationBean<>(); bean.setFilter(new SingleSignOutFilter()); bean.addUrlPatterns("/api/*"); bean.setOrder(Ordered.HIGHEST_PRECEDENCE + 5); return bean; } @Bean public FilterRegistrationBean casAuthenticationFilter() { FilterRegistrationBean bean = new FilterRegistrationBean<>(); AuthenticationFilter filter = new AuthenticationFilter(); filter.setCasServerLoginUrl(ssoProperties.getServerUrlPrefix() + "/login"); filter.setServerName(serverName()); bean.setFilter(filter); bean.addUrlPatterns("/api/*"); bean.setOrder(Ordered.HIGHEST_PRECEDENCE + 6); return bean; } @Bean public FilterRegistrationBean casValidationFilter() { FilterRegistrationBean bean = new FilterRegistrationBean<>(); Cas20ProxyReceivingTicketValidationFilter filter = new Cas20ProxyReceivingTicketValidationFilter(); filter.setServerName(serverName()); bean.setFilter(filter); // cas-client 3.5.1 的校验过滤器无 setCasServerUrlPrefix 设值器,需经 init-parameter 注入 bean.addInitParameter("casServerUrlPrefix", ssoProperties.getServerUrlPrefix()); bean.addUrlPatterns("/api/*"); bean.setOrder(Ordered.HIGHEST_PRECEDENCE + 7); return bean; } @Bean public FilterRegistrationBean casHttpServletRequestWrapperFilter() { FilterRegistrationBean bean = new FilterRegistrationBean<>(); bean.setFilter(new HttpServletRequestWrapperFilter()); bean.addUrlPatterns("/api/*"); bean.setOrder(Ordered.HIGHEST_PRECEDENCE + 8); return bean; } }