修复统一身份认证登录:补回从未提交的 gxmu.auth 配置
线上 /api/sso/login 报「学校统一身份认证未配置,请联系管理员」, /api/sso/available 返回 false。根因不是代码,是配置:gxmu.auth 段 只存在于本地工作区,从未进入任何一次提交。 证据:git log -S GXMU_AUTH_APPID 只命中两处,都不是 application.yml —— LyuapAuthClientIT 里硬编码的兜底值,以及 .scratch/student-sso/README.md 第 5.1 节那句「已写进 application.yml」。而 6f04a64(新增统一身份认证登录) 的 13 个文件全在 src/main/java 与 src/test,根本没碰 resources。 后果:GxmuAuthProperties.appId 默认为 null → available() 为 false → login() 在凭据校验处直接抛异常,请求根本到不了认证平台。 之所以单测全绿而功能全废:SsoLoginServiceImplTest 把 GxmuAuthProperties 整个 mock 掉了(getAppId 固定返回 "gxmu-app"),这类测试永远发现不了 「yml 里没这一段」。Dify 那批配置当初踩的也是同一类坑。 - application.yml 补回 gxmu.auth 段:enabled/base-url/app-id/app-secret/ timeout-ms/allow-unknown-user,与 openplat 凭证同样处理,默认值写入 并保留 GXMU_AUTH_APPID / GXMU_AUTH_APPSECRET 环境变量覆盖 - 新增 GxmuAuthPropertiesBindingTest:照 DifyProdProfileBindingTest 的做法 直接绑定 yml 断言,不启动容器、不依赖数据库与 Redis。四条用例先红后绿, 最后一条是端到端语义 —— 配置齐备时 available() 必须为 true,而不只是 「配置字段非空」 已联网验证(真实打到 cas.gxmu.edu.cn):available 由 false 变 true; 不存在的学号返回平台业务码「学号不存在,请确认后重试」,证明 appid 与 secret 已被平台认下。 注:TestMain / WebSoftApplicationTests 两项失败为预先存在, 已用 git stash 在改动前复现同一错误(serverEndpointExporter 的 ServerContainer not available),与本次改动无关。
This commit is contained in:
@@ -182,6 +182,21 @@ gxmu:
|
|||||||
timeout-ms: 30000
|
timeout-ms: 30000
|
||||||
max-retries: 2
|
max-retries: 2
|
||||||
page-size: 10000
|
page-size: 10000
|
||||||
|
# 学校统一身份认证(微服务认证平台 API v2.3),小程序的「学号 + 统一身份认证密码」登录走这里。
|
||||||
|
#
|
||||||
|
# appid/appsecret 由学校认证管理员在认证后台 - 应用管理 - 应用列表 中签发。
|
||||||
|
# 2026-09-18 拿到后只在本地验证过(见 .scratch/student-sso/README.md 第 5.1 节),
|
||||||
|
# 但这段配置当时漏了提交,代码上线后 /api/sso/login 一直报
|
||||||
|
# 「学校统一身份认证未配置,请联系管理员」——见本文件所在提交的说明。
|
||||||
|
# 与 openplat 凭证同样处理:默认值写在这里,同时保留环境变量覆盖。
|
||||||
|
auth:
|
||||||
|
enabled: true
|
||||||
|
base-url: https://cas.gxmu.edu.cn/lyuapServer
|
||||||
|
app-id: ${GXMU_AUTH_APPID:7D8F6695611309E0}
|
||||||
|
app-secret: ${GXMU_AUTH_APPSECRET:24f9dab5-3319-42cc-908d-2748417b7f92}
|
||||||
|
timeout-ms: 10000
|
||||||
|
# 名册外的账号默认拒绝登录,见 ADR-0001:学生须先同步进名册
|
||||||
|
allow-unknown-user: false
|
||||||
openplat-sync:
|
openplat-sync:
|
||||||
enabled: true
|
enabled: true
|
||||||
# 每天 03:30
|
# 每天 03:30
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package com.gxwebsoft.gxmu.auth;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.context.properties.bind.Binder;
|
||||||
|
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||||
|
import org.springframework.core.env.PropertySource;
|
||||||
|
import org.springframework.core.env.StandardEnvironment;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验 {@code gxmu.auth} 接入配置真的存在于 {@code application.yml} 并能绑定。
|
||||||
|
*
|
||||||
|
* <p>背景(2026-09-21 生产现象):
|
||||||
|
* {@code /api/sso/login} 报「学校统一身份认证未配置,请联系管理员」,
|
||||||
|
* 而 {@code /api/sso/available} 返回 {@code false}。查下来 appid/appsecret
|
||||||
|
* <b>从来没有进过仓库</b> —— 代码、单测({@code SsoLoginServiceImplTest} 全程 mock
|
||||||
|
* {@code GxmuAuthProperties})、技术说明({@code .scratch/student-sso/README.md} 第 5.1 节
|
||||||
|
* 写着「已写进 application.yml」)三者都齐了,唯独那份 yml 从未提交。
|
||||||
|
*
|
||||||
|
* <p>后果是:单测全绿、功能全废。这与 Dify 那批配置当初踩的是同一类坑,
|
||||||
|
* 所以照 {@code DifyProdProfileBindingTest} 的做法加一道绑定断言 ——
|
||||||
|
* 光靠 mock 掉 {@code properties} 的单元测试永远发现不了「yml 里没这一段」。
|
||||||
|
*
|
||||||
|
* <p>纯绑定测试,不启动 Spring 容器,不依赖数据库与 Redis,可直接跑。
|
||||||
|
*/
|
||||||
|
class GxmuAuthPropertiesBindingTest {
|
||||||
|
|
||||||
|
private GxmuAuthProperties load() throws Exception {
|
||||||
|
StandardEnvironment environment = new StandardEnvironment();
|
||||||
|
for (PropertySource<?> source : new YamlPropertySourceLoader()
|
||||||
|
.load("application", new ClassPathResource("application.yml"))) {
|
||||||
|
environment.getPropertySources().addLast(source);
|
||||||
|
}
|
||||||
|
return Binder.get(environment).bind("gxmu.auth", GxmuAuthProperties.class).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这一段一旦缺失,登录接口会在 {@code available()} 处直接抛出,
|
||||||
|
* 且只有真实启动应用才看得见 —— 因此必须由测试钉住。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void applicationYml中存在gxmu_auth配置段() throws Exception {
|
||||||
|
GxmuAuthProperties properties = load();
|
||||||
|
org.junit.jupiter.api.Assertions.assertNotNull(properties,
|
||||||
|
"application.yml 缺少 gxmu.auth 段:登录会报「学校统一身份认证未配置,请联系管理员」");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* appid / appsecret 是登录可用的充要条件,任一为空 {@code available()} 即为 false,
|
||||||
|
* 小程序端会拿到 500 与「未配置」提示。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void appid与appsecret均已配置且非空() throws Exception {
|
||||||
|
GxmuAuthProperties properties = load();
|
||||||
|
org.junit.jupiter.api.Assertions.assertNotNull(properties);
|
||||||
|
|
||||||
|
org.junit.jupiter.api.Assertions.assertTrue(properties.isEnabled(),
|
||||||
|
"gxmu.auth.enabled 应默认为 true,否则登录入口直接关闭");
|
||||||
|
org.junit.jupiter.api.Assertions.assertFalse(
|
||||||
|
cn.hutool.core.util.StrUtil.isBlank(properties.getAppId()),
|
||||||
|
"gxmu.auth.app-id 为空,请向学校认证管理员取得并写入 application.yml");
|
||||||
|
org.junit.jupiter.api.Assertions.assertFalse(
|
||||||
|
cn.hutool.core.util.StrUtil.isBlank(properties.getAppSecret()),
|
||||||
|
"gxmu.auth.app-secret 为空,请向学校认证管理员取得并写入 application.yml");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证平台地址由校方提供,绑错会表现为"无法连接"而非"未配置",同样难查。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void 认证平台地址指向学校lyuapServer() throws Exception {
|
||||||
|
GxmuAuthProperties properties = load();
|
||||||
|
org.junit.jupiter.api.Assertions.assertNotNull(properties);
|
||||||
|
|
||||||
|
org.junit.jupiter.api.Assertions.assertEquals("https://cas.gxmu.edu.cn/lyuapServer",
|
||||||
|
properties.getBaseUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 端到端语义:yml 里配了,{@code available()} 就必须为 true。
|
||||||
|
* 这条是本测试存在的理由 —— 前三条只证明"配置在",这条证明"配置够用"。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void 配置齐备时available返回true() throws Exception {
|
||||||
|
GxmuAuthProperties properties = load();
|
||||||
|
org.junit.jupiter.api.Assertions.assertNotNull(properties);
|
||||||
|
|
||||||
|
SsoLoginServiceImpl service = new SsoLoginServiceImpl();
|
||||||
|
org.springframework.test.util.ReflectionTestUtils.setField(service, "properties", properties);
|
||||||
|
|
||||||
|
org.junit.jupiter.api.Assertions.assertTrue(service.available(),
|
||||||
|
"yml 里配置齐备时 available() 必须为 true,否则小程序端会认为该登录方式不可用");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user