Files
CasSsoService/docker/nacos_config.py
T

188 lines
6.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# 启动前从 Nacos 拉取 common.yaml + cas.yaml,生成 /etc/cas/config/cas.properties。
# 替代失效的 EnvironmentPostProcessor(CAS 5.3 + Spring Cloud bootstrap 下 EPP 不触发)。
# CAS 5.3 原生加载 /etc/cas/config/cas.properties(profile=standalone),优先级高于 classpath application.yml。
import os, sys, json, urllib.request, urllib.parse, yaml
ADDR = os.environ.get("NACOS_ADDR", "nacos.yunhaixingtong.com:8848")
NS = os.environ.get("NACOS_NAMESPACE", "dev")
USER = os.environ.get("NACOS_USER", "yh-reader")
PASS = os.environ.get("NACOS_PASS", "123456")
OUT = os.environ.get("CAS_PROPERTIES", "/etc/cas/config/cas.properties")
# 库名默认值(cas.yaml 的 cas.authn.jdbc.query[0].database 仅用于拼 jdbc url,不作为独立属性写入)
DEFAULT_DB = "bsch_userbase"
DEFAULT_PARAMS = "useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"
# CAS 不识别的属性(写入会导致 RelaxedBinding 崩溃),从输出中排除
EXCLUDE_KEYS = {"cas.authn.jdbc.query[0].database", "cas.authn.jdbc.query[0].dialect"}
# 本地兜底配置(与运行环境无关,烤进 cas.properties)
LOCAL_CONFIG = """server.port: 8443
server.ssl.enabled: true
server.ssl.key-store: /etc/cas/cas.keystore
server.ssl.key-store-password: changeit
server.ssl.key-password: changeit
server.ssl.key-alias: cas
logging.config: file:/etc/cas/config/log4j2.xml
cas.adminPagesSecurity.ip=127\\.0\\.0\\.1
"""
def http_get(url, timeout=10):
try:
with urllib.request.urlopen(url, timeout=timeout) as r:
return r.read().decode("utf-8") if r.status == 200 else None
except Exception:
return None
def http_post(url, data=b"", timeout=10):
try:
req = urllib.request.Request(url, data=data, method="POST")
with urllib.request.urlopen(req, timeout=timeout) as r:
return r.read().decode("utf-8") if r.status == 200 else None
except Exception:
return None
def login():
url = "http://%s/nacos/v1/auth/login?username=%s&password=%s" % (
ADDR, urllib.parse.quote(USER), urllib.parse.quote(PASS))
resp = http_post(url)
if not resp:
return None
try:
return json.loads(resp).get("accessToken")
except Exception:
i = resp.find('"accessToken"')
if i < 0:
return None
s = resp.find('"', i + 14)
e = resp.find('"', s + 1)
return resp[s + 1:e] if s >= 0 and e > s else None
def resolve_ns(token):
if not NS:
return ""
url = "http://%s/nacos/v1/console/namespaces" % ADDR
if token:
url += "?accessToken=" + urllib.parse.quote(token)
body = http_get(url)
if not body:
return NS
try:
data = json.loads(body).get("data", [])
except Exception:
return NS
for item in data:
if NS == item.get("namespaceShowName") or NS == item.get("namespace"):
return item.get("namespace", "")
return NS
def fetch_yaml(data_id, group, nsid, token):
base = "http://%s/nacos/v1/cs/configs" % ADDR
qs = "dataId=%s&group=%s&tenant=%s" % (
urllib.parse.quote(data_id), urllib.parse.quote(group), urllib.parse.quote(nsid))
if token:
content = http_get("%s?%s&accessToken=%s" % (base, qs, urllib.parse.quote(token)))
if content and content.strip() and '"error"' not in content:
return content
return http_get("%s?%s" % (base, qs)) or ""
def flatten(prefix, value, out):
if isinstance(value, dict):
for k, v in value.items():
flatten(k if not prefix else prefix + "." + k, v, out)
elif isinstance(value, list):
for i, v in enumerate(value):
flatten("%s[%d]" % (prefix, i) if prefix else "[%d]" % i, v, out)
elif value is not None:
out[prefix] = value
def parse_yaml(content):
if not content or not content.strip():
return {}
data = yaml.safe_load(content) or {}
flat = {}
flatten("", data, flat)
return flat
def esc(v):
if isinstance(v, bool):
return "true" if v else "false"
s = str(v)
s = s.replace("\\", "\\\\")
return s
def build(common, cas):
merged = {}
# cas.yaml 透传(排除无效属性)
for k, v in cas.items():
if k not in EXCLUDE_KEYS:
merged[k] = v
# datasource -> jdbc url(与 EPP buildCasProperties 逻辑一致)
ds_host = common.get("spring.datasource.host") or common.get("mysql.host")
ds_port = common.get("spring.datasource.port") or common.get("mysql.port") or 3306
ds_user = common.get("spring.datasource.username") or common.get("mysql.user")
ds_pass = common.get("spring.datasource.password") or common.get("mysql.password")
ds_params = common.get("spring.datasource.params") or common.get("mysql.params") or DEFAULT_PARAMS
db_name = cas.get("cas.authn.jdbc.query[0].database", DEFAULT_DB)
if ds_host:
jdbc_url = "jdbc:mysql://%s:%s/%s?%s" % (ds_host, ds_port, db_name, ds_params)
merged["cas.authn.jdbc.query[0].url"] = jdbc_url
if ds_user:
merged["cas.authn.jdbc.query[0].user"] = ds_user
if ds_pass:
merged["cas.authn.jdbc.query[0].password"] = ds_pass
merged["cas.authn.jdbc.query[0].driverClass"] = "com.mysql.jdbc.Driver"
# 透传 redis / rabbitmq
for k, v in common.items():
if k.startswith("spring.redis.") or k.startswith("spring.rabbitmq."):
merged[k] = v
return merged
def main():
token = login()
nsid = resolve_ns(token)
common_raw = fetch_yaml("common.yaml", "DEFAULT_GROUP", nsid, token)
cas_raw = fetch_yaml("cas.yaml", "CAS", nsid, token)
common = parse_yaml(common_raw)
cas = parse_yaml(cas_raw)
if not common and not cas:
print("[nacos_config] WARN: 未拉到任何 Nacos 配置,保留现有 cas.properties", file=sys.stderr)
sys.exit(1)
merged = build(common, cas)
lines = [
"# 由 nacos_config.py 从 Nacos 生成(勿手改)",
"# namespace=%s nsid=%s common_keys=%d cas_keys=%d" % (NS, nsid, len(common), len(cas)),
]
for k in sorted(merged.keys()):
lines.append("%s=%s" % (k, esc(merged[k])))
lines.append("")
lines.append("# === 本地兜底配置 ===")
lines.append(LOCAL_CONFIG.rstrip("\n"))
content = "\n".join(lines) + "\n"
os.makedirs(os.path.dirname(OUT), exist_ok=True)
with open(OUT, "w", encoding="utf-8") as f:
f.write(content)
print("[nacos_config] wrote %d props -> %s" % (len(merged), OUT))
print("[nacos_config] always-captcha=%s redis.host=%s jdbc.host=%s server.name=%s" % (
merged.get("custom.login.always-captcha"),
merged.get("spring.redis.host"),
common.get("spring.datasource.host"),
merged.get("cas.server.name")))
if __name__ == "__main__":
main()