SSO 地址去掉 /cas 前缀:context-path 改为根路径,登录地址 /cas/login 变为 /login
build / build-push (push) Successful in 3m33s

This commit is contained in:
2026-09-03 22:28:28 +08:00
parent 229067d518
commit 6ceaed63d6
12 changed files with 57 additions and 37 deletions
+2 -2
View File
@@ -6,9 +6,9 @@ CAS 5.3.16 单点登录服务(war overlay 项目,源自 CasSsoService 改版
- **构建产物是 WAR 不是 jar**:pom `packaging=war`、`finalName=cas`,CI 在 maven 构建后 `cp cas.war app.jar`(可执行 WAR,WarLauncher 直接 `java -jar`)。
- **Dockerfile 为 CAS 专用**:镜像内生成自签名 keystore(8443 HTTPS),内置 `docker/cas.properties`(仅 TLS/端口/日志)与 `etc/cas/config/log4j2.xml`;业务配置由 projman 配置管理挂载 `/app/config/application.yml`。
- **健康检查**:`/cas/status`(HTTPS 自签名),projman 应用需配置 healthPath=`/cas/status` + HTTPS 开。
- **健康检查**:`/status`(HTTPS 自签名),projman 应用需配置 healthPath=`/status` + HTTPS 开。(context-path 已改为根 `/`,登录地址为 `https://sso.yunhaixingtong.com/login`,不再有 `/cas` 前缀;健康检查从 `/cas/status` 变为 `/status`。)
- **pom 不声明外网仓库**:依赖全部经 nexus maven-public 代理解析(内网 CI 无外网)。
- **登录页静态文件随 WAR 打包**:`src/main/webapp/front/`(logo、native-login.js 等)入库并打进 cas.war,由 CAS 同源托管在 `/cas/front/*`。原独立的 CasSsoFront 静态仓**已废弃不用**——其中没有 `native-login.js`(登录逻辑只在本仓),Caddy 磁盘托管仅为灰度兜底。
- **登录页静态文件随 WAR 打包**:`src/main/webapp/front/`(logo、native-login.js 等)入库并打进 cas.war,由 CAS 同源托管在 `/front/*`。原独立的 CasSsoFront 静态仓**已废弃不用**——其中没有 `native-login.js`(登录逻辑只在本仓)。
## 推送后验证
+3 -2
View File
@@ -2,8 +2,9 @@
@set JAVA_ARGS=-Xms500m -Xmx1g
@if "%CAS_SERVER_NAME%" == "" set CAS_SERVER_NAME=http://localhost:8443
@if "%CAS_SERVER_PREFIX%" == "" set CAS_SERVER_PREFIX=http://localhost:8443/cas
@set CAS_JAVA_OVERRIDES=-Dserver.use-forward-headers=false -Dcas.server.name=%CAS_SERVER_NAME% -Dcas.server.prefix=%CAS_SERVER_PREFIX%
@if "%CAS_SERVER_PREFIX%" == "" set CAS_SERVER_PREFIX=http://localhost:8443
@rem 根 context-path 必须用空字符串(Boot 1.5 显式拒绝 "/":Root ContextPath must be specified using an empty string)
@set CAS_JAVA_OVERRIDES=-Dserver.use-forward-headers=false -Dserver.context-path= -Dcas.server.name=%CAS_SERVER_NAME% -Dcas.server.prefix=%CAS_SERVER_PREFIX%
@set CAS_DIR=\etc\cas
@set CONFIG_DIR=\etc\cas\config
+1 -1
View File
@@ -1,7 +1,7 @@
# CAS 5.3.16 运行镜像(单阶段,war 预构建)
# CI 已先 mvn package 并在容器内把产物 cp 成固定名 target/app.jar(= cas.war,可执行 WAR)
# 构建上下文 = 仓库根:docker build -f docker/Dockerfile .
# 健康检查(projman 应用配置 healthPath=/cas/status + HTTPS 自签名开):curl -fk https://localhost:8443/cas/status
# 健康检查(projman 应用配置 healthPath=/status + HTTPS 自签名开):curl -fk https://localhost:8443/status
# 时区 Asia/Shanghai;JAVA_OPTS 由 projman 部署 env 注入(默认 -Xms512m -Xmx1024m)
FROM eclipse-temurin:8-jre
@@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration;
/**
* 将自定义数据库认证处理器注册进 CAS 认证执行计划,
* 使标准登录表单(/cas/login)的认证链包含数据库校验。
* 使标准登录表单(/login)的认证链包含数据库校验。
*/
@Configuration("DbAuthenticationConfiguration")
public class DbAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer {
@@ -20,7 +20,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* 自定义数据库认证处理器:让 CAS 标准登录表单(/cas/login)也走数据库校验。
* 自定义数据库认证处理器:让 CAS 标准登录表单(/login)也走数据库校验。
* 校验逻辑与 /api/login 完全一致(BCrypt + sys_user 表),保证两套登录行为统一。
* 用原生 DriverManager 直连,不依赖 CAS 内置 JdbcAuthenticationHandler 的连接池/模板,
* 因此不受 WAR 中 spring-jdbc 双版本等潜在初始化问题影响。
@@ -386,7 +386,7 @@ public class LoginController {
writeJson(response, buildResponse(1, data, msg));
} else {
// 原生表单提交:302 回登录页并带错误提示;同时让浏览器在成功时记住密码。
String target = "/cas/login?error=" + URLEncoder.encode(msg, "UTF-8");
String target = "/login?error=" + URLEncoder.encode(msg, "UTF-8");
response.sendRedirect(target);
}
}
@@ -504,7 +504,7 @@ public class LoginController {
return svcUrl + (svcUrl.contains("?") ? "&" : "?") + "ticket=" + st.getId();
}
// 返回绝对 URL,确保前端无论从哪个 origin 打开都能正确跳回 CAS 同源,
// 从而把 TGC Cookie 一起带过去(相对路径 /cas/login 在非同源前端下会跳错 origin)。
// 从而把 TGC Cookie 一起带过去(相对路径 /login 在非同源前端下会跳错 origin)。
return buildDefaultFallbackUrl(request);
} catch (AuthenticationException | AbstractTicketException e) {
logger.error("用户 {} 建立 SSO 会话失败", username, e);
@@ -518,16 +518,29 @@ public class LoginController {
/**
* 构造默认落点(无 service / service 未注册 / 未配置时的回退地址)。
* 默认落点若未配置、或误配成 CAS 原生登录页路径,则强制回退到业务系统首页,
* 避免出现“登录成功却被甩回 /cas/login”的死循环(曾因 application.yml 误配踩过)。
* 避免出现“登录成功却被甩回登录页”的死循环(曾因 application.yml 误配踩过)。
*/
private String buildDefaultFallbackUrl(HttpServletRequest request) {
String target = defaultRedirectUrl;
if (target == null || target.trim().isEmpty() || target.contains("/cas/login")) {
if (target == null || target.trim().isEmpty() || isCasLoginPath(target)) {
target = "https://base.yunhaixingtong.com";
}
return buildCasAbsoluteUrl(request, target);
}
/**
* 判断地址是否指向 CAS 自身登录页(剥掉查询串后以 /login 或 /cas/login 结尾,后者兼容历史配置),
* 用于拦截误把登录页配成默认落点导致的“登录成功却被甩回登录页”死循环。
*/
private boolean isCasLoginPath(String url) {
String path = url;
int q = path.indexOf('?');
if (q >= 0) {
path = path.substring(0, q);
}
return path.endsWith("/login") || path.endsWith("/cas/login");
}
/**
* 基于当前请求推断 CAS 自身的访问基地址(scheme://host:port),用于构造绝对跳转 URL,
* 避免前端页面不在 CAS 同源时相对路径跳转错 origin,导致 TGC Cookie 无法随跳转携带。
+12 -6
View File
@@ -3,21 +3,27 @@ server:
enabled: false
port: 8443
use-forward-headers: false
# context-path 必须为根(登录地址 https://sso.yunhaixingtong.com/login,去掉 /cas 前缀)。
# 注意:不能在这里写 server.context-path —— overlay WAR 自带的 classpath application.properties
# 显式设了 server.context-path=/cas,同在 WAR 内时 properties 优先级高于 yml,写了会被覆盖。
# 且 Boot 1.5 规定根 context 必须用空字符串("/" 会抛 Root ContextPath must be specified
# using an empty string)。因此该键只能由外部提供:本地 build.cmd 以 -Dserver.context-path=
# 注入(系统属性优先级最高),生产由 projman 挂载的外部配置(优先于 WAR 内 classpath)设空值。
cas:
# 显式声明 CAS 自身访问地址,避免根路径 / 或 /index.html 被重定向到错误端口(曾出现跳到 8080 的问题)。
# CAS 的默认值是 https://cas.example.org:8443,若运行时被外部配置覆盖成 8080,根页面重定向就会错位。
# prefix 不带 /cas(与根 context-path 一致),CAS 内部生成的登录/注销等 URL 均由它派生。
server:
name: http://localhost:8443
prefix: http://localhost:8443/cas
prefix: http://localhost:8443
# CAS 核心配置
tgc:
secure: false
# 显式指定 TGC Cookie 的 Path,必须覆盖 /cas 下所有路径(含自定义 /cas/api/login)。
# 若不配置,CAS 默认会把 Path 留空,浏览器按“请求目录”取值:
# - 标准登录在 /cas/login 种 Cookie -> Path=/cas/ -> 正常
# - 自定义登录在 /cas/api/login 种 Cookie -> Path=/cas/api/ -> 跳到 /cas/login 时浏览器不带此 Cookie,导致还要再登录
path: /cas
# 显式指定 TGC Cookie 的 Path。context-path 为根后,登录页在 /login、接口在 /api/*,
# TGC 必须对全站可见,故固定 Path=/。若留空,浏览器按“请求目录”取值,行为依赖隐式推导,不可靠。
# (历史版本曾配 /cas 以兼容 /cas/api/login 种 Cookie,context-path 改根后同步改为 /。)
path: /
# 固定 TGC 加密/签名密钥。不配置的话每次重启都会自动生成新密钥,
# 导致浏览器里旧 TGC Cookie 解密失败(表现为启动后首次访问的 NPE DEBUG 噪声),且启动日志反复 WARN。
crypto:
@@ -4,7 +4,7 @@
<head>
<meta charset="utf-8" />
<title>请稍候</title>
<link rel="icon" type="image/x-icon" href="/cas/front/image/favicon.ico?v=1" />
<link rel="icon" type="image/x-icon" href="/front/image/favicon.ico?v=1" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
@@ -12,17 +12,17 @@
<body>
<!--
覆盖 CAS 原生 casGenericSuccessView.html:已有有效 TGT 后再访问 /cas/login(无 service)
覆盖 CAS 原生 casGenericSuccessView.html:已有有效 TGT 后再访问 /login(无 service)
时,CAS 不再渲染默认"您已经成功登录中央认证系统"页,而是自动跳转到配置的默认落点
(custom.login.default-redirect-url,即 base.yunhaixingtong.com)。
跳转地址复用 /cas/api/check-sso 的返回(跟随 Nacos 配置,不硬编码);接口异常时兜底。
跳转地址复用 /api/check-sso 的返回(跟随 Nacos 配置,不硬编码);接口异常时兜底。
-->
<script>
(function () {
var fallback = "https://base.yunhaixingtong.com";
try {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/cas/api/check-sso", true);
xhr.open("GET", "/api/check-sso", true);
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) return;
try {
@@ -8,13 +8,13 @@
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<title>云海教育 · 统一身份认证平台</title>
<link rel="icon" type="image/x-icon" href="/cas/front/image/favicon.ico?v=1" />
<link rel="stylesheet" href="/cas/front/css/login.css?v=1.5" />
<script src="/cas/front/libs/jquery.min.js?v=2.1"></script>
<script src="/cas/front/js/jsencrypt.min.js?v=2.1"></script>
<script src="/cas/front/js/conf.js?v=2.3"></script>
<script src="/cas/front/js/bschlogin.js?v=2.1"></script>
<script src="/cas/front/js/native-login.js?v=3.2"></script>
<link rel="icon" type="image/x-icon" href="/front/image/favicon.ico?v=1" />
<link rel="stylesheet" href="/front/css/login.css?v=1.5" />
<script src="/front/libs/jquery.min.js?v=2.1"></script>
<script src="/front/js/jsencrypt.min.js?v=2.1"></script>
<script src="/front/js/conf.js?v=2.3"></script>
<script src="/front/js/bschlogin.js?v=2.1"></script>
<script src="/front/js/native-login.js?v=3.2"></script>
</head>
<body>
@@ -22,7 +22,7 @@
<aside class="login-banner">
<div class="banner-inner">
<div class="brand">
<img class="brand-logo" src="/cas/front/image/logo.png" alt="logo" />
<img class="brand-logo" src="/front/image/logo.png" alt="logo" />
<span class="brand-name">云海教育</span>
<span class="brand-slogan">云海星通</span>
</div>
+1 -1
View File
@@ -68,7 +68,7 @@ BschLogin.prototype = {
if (resp.code == 0) {
var url = (resp.data && resp.data.url) || "";
// 兜底:后端未返回有效地址或返回了 CAS 原生登录页路径时,跳回同源根
if (!url || url.indexOf("/cas/login") === 0) {
if (!url || url.indexOf("/cas/login") === 0 || url.indexOf("/login") === 0) {
url = "/";
}
console.log("login success, redirect =>", url);
+2 -2
View File
@@ -1,6 +1,6 @@
// CAS context-path 为根 "/",前端由 CAS 同源托管在 /front/,接口在 /api/*。
// context-path 为根 "/",前端由 CAS 同源托管在 /front/,接口在 /api/*。
// 用同源相对路径,避免写死 host/协议,且天然规避跨域(LoginController 未开 CORS)。
window.ssoServerHost = "/cas/";
window.ssoServerHost = "/";
window.beianText = "©2021 bestsch.com inc All Rights reserved";
window.beianLink = "【沪ICP备13000443号-1】";
+5 -5
View File
@@ -1,16 +1,16 @@
/**
* CAS 原生登录页(templates/casLoginView.html)配套脚本。
*
* 页面由 /cas/login 直接渲染,逻辑分三步:
* 页面由 /login 直接渲染,逻辑分三步:
* 1. onload 先调 GET {ssoServerHost}api/check-sso 预检:
* - 已有有效 TGT 且带 service → 直接跳 data.url(service?ticket=ST-xxx),单点免密回业务系统;
* - 已有有效 TGT 无 service → 跳默认落点(/cas/front/index.html);
* - 已有有效 TGT 无 service → 跳默认落点(Nacos 配置 custom.login.default-redirect-url);
* - 未登录/接口异常 → 继续初始化登录表单(fail 降级保证灰度期旧后端也能用)。
* 2. 初始化:公钥加载、验证码显隐、error= 查询串回显历史错误。
* 3. 提交:走 bschlogin.login()(RSA+POST /cas/api/login),成功后 JS 直接跳 data.url;
* 3. 提交:走 bschlogin.login()(RSA+POST /api/login),成功后 JS 直接跳 data.url;
* 失败展示 msg,且 needCaptcha=true 时自动展开验证码面板。
*
* 资源与接口全部使用 conf.js 的 window.ssoServerHost("/cas/"),同源相对,避免跨域。
* 资源与接口全部使用 conf.js 的 window.ssoServerHost("/"),同源相对,避免跨域。
*/
(function () {
@@ -71,7 +71,7 @@
bschLogin.isNeedCaptcha(setCaptcha);
var err = bschLogin.getQueryVariable("error");
if (err) {
// 非 AJAX 提交失败时后端会 302 回 /cas/login?error=<encode后的提示>
// 非 AJAX 提交失败时后端会 302 回 /login?error=<encode后的提示>
try {
err = decodeURIComponent(err);
} catch (e) {