静态资源更改
build / build-push (push) Successful in 44s

This commit is contained in:
2026-08-31 22:00:07 +08:00
parent dc8a0924a8
commit 41d7d98c77
8 changed files with 381 additions and 3 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
# 前端由 CasSsoFront 仓库独立发布,本后端仓库只发纯后端(cas.war)
src/main/webapp/front/
# 登录页静态资源(src/main/webapp/front/)随 WAR 打包提供:其中 native-login.js 等
# 仅存在于后端仓,独立的 CasSsoFront 静态仓没有,必须入库才能保证 CI 构建的 war 自包含
*.iml
.idea/
# 构建产物与二进制不入库(CI 用 maven 从源码构建 cas.war)
+1 -1
View File
@@ -8,7 +8,7 @@ CAS 5.3.16 单点登录服务(war overlay 项目,源自 CasSsoService 改版
- **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 开。
- **pom 不声明外网仓库**:依赖全部经 nexus maven-public 代理解析(内网 CI 无外网)。
- 前端登录页静态文件由 CasSsoFront 仓库独立发布(`src/main/webapp/front/` 不入库)。
- **登录页静态文件随 WAR 打包**:`src/main/webapp/front/`(logo、native-login.js 等)入库并打进 cas.war,由 CAS 同源托管在 `/cas/front/*`。原独立的 CasSsoFront 静态仓**已废弃不用**——其中没有 `native-login.js`(登录逻辑只在本仓),Caddy 磁盘托管仅为灰度兜底。
## 推送后验证
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+164
View File
@@ -0,0 +1,164 @@
function BschLogin(ssoServerHost) {
this.isDebug = false;
this.ssoServerHost = ssoServerHost;
this.captchaUrl = ssoServerHost + "api/captcha";
this.loginUrl = ssoServerHost + "api/login";
this.publicKeyUrl = ssoServerHost + "api/public-key";
this.needCaptchaUrl = ssoServerHost + "api/need-captcha";
this.publicKey = "";
// this.setCookie("loginPageTag", loginPageTag, 30);
}
BschLogin.prototype = {
getPublicKey: function () {
var _this = this;
if (_this.isDebug) {
_this.publicKey =
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqytbHgQWZQmkwmp6WncVaUmUNgExYDB0Sp8h1qfyKfufbX2MEM6UzSPQHIwurFqkzcHljdrGDCXWZ2Xv8c8C27+MOPLf4VyYQ7invqtvmEwS6c3km4QqQVdrh0DJjVYhgO8reE8LhlDFc9Vh9FwySb8pbC1EM0s2lN6ig7/9NR8+CqxW61ku+7VYnLzcRgpc0cF3K/mQwAjVf1pKjGtPG1mpLNcHpT2yp5u950GfFtdqYgAte7k4CDlwMDKfpf+2Pg5kZAzogO5DTq/QolOWqjZfgT2wBNpYVT+3glbhhRbc9T3/SqFeT9KlXcamBXLvKI6WuKxlAEeKTiosUgMKCQIDAQAB";
return;
}
$.get(
_this.publicKeyUrl,
function (resp) {
_this.publicKey = resp.data.publicKey;
},
"json",
);
},
//是否需要验证校验码
isNeedCaptcha: function (callback) {
if (this.isDebug) return callback(false);
$.get(
this.needCaptchaUrl,
function (resp) {
if (resp.code == 0) {
var needCaptcha = resp.data.needCaptcha;
callback(needCaptcha);
} else {
callback(true);
}
},
"json",
);
},
//登录
login: function (username, pwd, captcha, onSuccess, onFailed) {
var service = this.getQueryVariable("service");
var encrypt = new JSEncrypt();
encrypt.setPublicKey(this.publicKey);
username = encrypt.encrypt(username);
pwd = encrypt.encrypt(pwd);
var params = {
loginname: username,
pwd: pwd,
captcha: captcha,
};
if (typeof service === "string") params.service = service;
console.log(JSON.stringify(params));
$.ajax({
url: this.loginUrl,
type: "POST",
data: params,
dataType: "json",
success: function (resp, textStatus, jqXHR) {
if (resp && typeof resp.code !== "undefined") {
if (resp.code == 0) {
var url = (resp.data && resp.data.url) || "";
// 兜底:后端未返回有效地址或返回了 CAS 原生登录页路径时,跳回同源根
if (!url || url.indexOf("/cas/login") === 0) {
url = "/";
}
console.log("login success, redirect =>", url);
onSuccess(url);
} else {
var needCaptcha = resp.data ? resp.data.needCaptcha : false;
onFailed(resp.msg, needCaptcha);
}
return;
}
onSuccess(jqXHR.responseURL || "");
},
error: function (jqXHR) {
var resp = jqXHR.responseJSON;
if (resp && typeof resp.code !== "undefined") {
var needCaptcha = resp.data ? resp.data.needCaptcha : false;
onFailed(resp.msg, needCaptcha);
return;
}
onFailed("登录请求失败", false);
},
});
},
//请求短信验证码
requestSmsCode: function (phoneCode, onSuccess, onFailed) {
var params = {
phone: phoneCode,
};
$.get(
this.smsCodeUrl,
params,
function (resp) {
if (resp.code == 0) {
onSuccess();
} else {
onFailed(resp.msg);
}
},
"json",
);
},
//按短信验证码,修改密码
updatePwd: function (phoneCode, smsCode, newPwd, onSuccess, onFailed) {
var params = {
phone: phoneCode,
smsCode: smsCode,
newPwd: newPwd,
};
$.post(
this.updatePwdUrl,
params,
function (resp) {
if (resp.code == 0) {
onSuccess();
} else {
onFailed(resp.msg);
}
},
"json",
);
},
captchaSrc: function () {
return this.captchaUrl + "?k=" + new Date().getTime();
},
getQueryVariable: function (paramName) {
var query = window.location.search.substring(1);
var params = query.split("&");
for (var i = 0; i < params.length; i++) {
var param = params[i];
var pos = param.indexOf("=");
if (pos == -1) continue;
var key = param.slice(0, pos);
var value = param.slice(pos + 1, param.length);
if (key == paramName) return value;
}
return "";
},
setCookie: function (c_name, value, expiredays) {
var exdate = new Date();
exdate.setDate(exdate.getDate() + expiredays);
document.cookie =
c_name + "=" + escape(value) + (expiredays == null ? "" : ";expires=" + exdate.toGMTString()) + ";path=/"; //+";domain=";
},
};
+7
View File
@@ -0,0 +1,7 @@
// CAS context-path 为根 "/",前端由 CAS 同源托管在 /front/,接口在 /api/*。
// 用同源相对路径,避免写死 host/协议,且天然规避跨域(LoginController 未开 CORS)。
window.ssoServerHost = "/cas/";
window.beianText = "©2021 bestsch.com inc All Rights reserved";
window.beianLink = "【沪ICP备13000443号-1】";
window.logoHeight = 35;
File diff suppressed because one or more lines are too long
+199
View File
@@ -0,0 +1,199 @@
/**
* CAS 原生登录页(templates/casLoginView.html)配套脚本。
*
* 页面由 /cas/login 直接渲染,逻辑分三步:
* 1. onload 先调 GET {ssoServerHost}api/check-sso 预检:
* - 已有有效 TGT 且带 service → 直接跳 data.url(service?ticket=ST-xxx),单点免密回业务系统;
* - 已有有效 TGT 无 service → 跳默认落点(/cas/front/index.html);
* - 未登录/接口异常 → 继续初始化登录表单(fail 降级保证灰度期旧后端也能用)。
* 2. 初始化:公钥加载、验证码显隐、error= 查询串回显历史错误。
* 3. 提交:走 bschlogin.login()(RSA+POST /cas/api/login),成功触发"记住密码"iframe 后
* JS 手动跳 data.url;失败展示 msg,且 needCaptcha=true 时自动展开验证码面板。
*
* 资源与接口全部使用 conf.js 的 window.ssoServerHost("/cas/"),同源相对,避免跨域。
*/
(function () {
var bschLogin;
function setWarning(msg) {
document.getElementById("warningLabel").textContent = msg || "";
}
function setCaptcha(isVisible) {
var panel = document.getElementById("captchaPanel");
if (isVisible) {
document.getElementById("captchaImg").src = bschLogin.captchaSrc();
panel.style.display = "block";
panel.dataset.captcha = "true";
} else {
panel.style.display = "none";
panel.dataset.captcha = "false";
}
}
function refreshCaptcha() {
document.getElementById("captchaImg").src = bschLogin.captchaSrc();
}
function initLoginForm() {
bschLogin.getPublicKey();
bschLogin.isNeedCaptcha(setCaptcha);
var err = bschLogin.getQueryVariable("error");
if (err) {
// 非 AJAX 提交失败时后端会 302 回 /cas/login?error=<encode后的提示>
try {
err = decodeURIComponent(err);
} catch (e) {
/* 保持原值 */
}
setWarning(err);
}
document.getElementById("user_input").focus();
}
// SSO 预检:命中有效会话直接免密跳转,这是恢复单点登录的关键一步
function checkSsoBeforeForm(service, done) {
if (!$ || !$.get) {
done();
return;
}
$.get(
window.ssoServerHost + "api/check-sso",
service ? { service: service } : {},
function (resp) {
if (resp && resp.code == 0 && resp.data && resp.data.loggedIn && resp.data.url) {
console.log("check-sso hit sso session, redirect =>", resp.data.url);
window.location.replace(resp.data.url);
return;
}
done();
},
"json",
).fail(done);
}
function triggerPasswordSave(username, pwd, encUser, encPwd, captcha) {
try {
document.getElementById("pm_user").value = username;
document.getElementById("pm_pwd").value = pwd;
document.getElementById("pm_loginname").value = encUser;
document.getElementById("pm_pwd_enc").value = encPwd;
document.getElementById("pm_captcha").value = captcha;
document.getElementById("pm_service").value =
document.getElementById("service_hidden").value;
document.getElementById("pwdMgrForm").submit();
} catch (e) {
/* 记住密码属最佳努力,失败不影响主流程 */
}
}
function doLogin() {
var username = document.getElementById("user_input").value.trim();
var pwd = document.getElementById("password_input").value;
var captcha = document.getElementById("captcha_input").value;
var needCaptcha = document.getElementById("captchaPanel").dataset.captcha === "true";
if (!username) {
setWarning("请输入用户名!");
return;
}
if (!pwd) {
setWarning("请输入密码!");
return;
}
if (needCaptcha && !captcha) {
setWarning("请输入验证码!");
return;
}
if (!bschLogin.publicKey) {
setWarning("公钥加载中,请稍候重试");
return;
}
// 隐藏 iframe 的"记住密码"提交要求 loginname/pwd 为 RSA 密文(后端必填),这里预生成
var encUser = null,
encPwd = null;
try {
var jsenc = new JSEncrypt();
jsenc.setPublicKey(bschLogin.publicKey);
encUser = jsenc.encrypt(username);
encPwd = jsenc.encrypt(pwd);
} catch (e) {
/* 最佳努力:失败仅影响记住密码,不影响主流程 */
}
var btn = document.getElementById("loginBtn");
btn.disabled = true;
btn.textContent = "登录中...";
setWarning("");
bschLogin.login(
username,
pwd,
captcha,
function (url) {
// 成功:最佳努力让浏览器记住密码(隐藏 iframe 真实提交),再手动跳转
triggerPasswordSave(username, pwd, encUser, encPwd, captcha);
// 页面存在 #loginSuccess 遮罩(完整版视觉)则展示;否则退回文字提示
var sc = document.getElementById("loginSuccess");
if (sc) {
sc.style.display = "flex";
} else {
setWarning("登录成功,正在跳转…");
}
setTimeout(function () {
window.location.href = url;
}, 450);
},
function (msg, nc) {
btn.disabled = false;
btn.textContent = "登录";
setWarning(msg || "登录失败");
if (nc) {
setCaptcha(true);
} else if (needCaptcha) {
// 验证码一次性消费,无论是否要求都必须换一张
refreshCaptcha();
}
},
);
}
window.onload = function () {
// 占位保留:bschlogin.login() 内部自行读取 service 参数并提交给后端
document.getElementById("service_hidden").value =
bschLogin.getQueryVariable("service") || "";
checkSsoBeforeForm(document.getElementById("service_hidden").value, initLoginForm);
};
// 暴露给模板内联 onclick 使用
window.doLogin = doLogin;
window.refreshCaptcha = refreshCaptcha;
window.onerror = function (msg) {
setWarning("页面脚本异常:" + msg);
return false;
};
document.addEventListener("DOMContentLoaded", function () {
bschLogin = new BschLogin(window.ssoServerHost);
document.getElementById("user_input").addEventListener("keydown", function (e) {
if (e.key === "Enter") document.getElementById("password_input").focus();
});
document.getElementById("password_input").addEventListener("keydown", function (e) {
if (e.key === "Enter") {
if (document.getElementById("captchaPanel").style.display !== "none") {
document.getElementById("captcha_input").focus();
} else {
doLogin();
}
}
});
document.getElementById("captcha_input").addEventListener("keydown", function (e) {
if (e.key === "Enter") doLogin();
});
});
})();
File diff suppressed because one or more lines are too long