From ed58d8a87057be7d154e1fd19d6921448d87faad Mon Sep 17 00:00:00 2001 From: chenyuan Date: Mon, 3 Aug 2026 16:26:13 +0800 Subject: [PATCH] fix: preserve cas timeout semantics --- .../application/CasTicketValidator.java | 10 ++++++- .../JdbcPlatformIdentityRepository.java | 4 +++ .../application/CasTicketValidatorTest.java | 26 +++++++++++++++++++ .../JdbcPlatformIdentityRepositoryTest.java | 13 ++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/yau/digitalrmb/platformintegration/application/CasTicketValidator.java b/src/main/java/com/yau/digitalrmb/platformintegration/application/CasTicketValidator.java index 6dd3d86..23098d8 100644 --- a/src/main/java/com/yau/digitalrmb/platformintegration/application/CasTicketValidator.java +++ b/src/main/java/com/yau/digitalrmb/platformintegration/application/CasTicketValidator.java @@ -6,6 +6,7 @@ import com.yau.digitalrmb.shared.exception.BusinessException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.http.ResponseEntity; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import org.w3c.dom.Document; @@ -26,7 +27,14 @@ public class CasTicketValidator { @Autowired public CasTicketValidator(PlatformIntegrationProperties properties) { - this(properties.getCas(), new RestTemplate()); + this(properties.getCas(), createRestTemplate()); + } + + private static RestTemplate createRestTemplate() { + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + requestFactory.setConnectTimeout(5000); + requestFactory.setReadTimeout(5000); + return new RestTemplate(requestFactory); } CasTicketValidator(PlatformIntegrationProperties.Cas properties, RestTemplate restTemplate) { diff --git a/src/main/java/com/yau/digitalrmb/platformintegration/infrastructure/JdbcPlatformIdentityRepository.java b/src/main/java/com/yau/digitalrmb/platformintegration/infrastructure/JdbcPlatformIdentityRepository.java index d708811..9a6a282 100644 --- a/src/main/java/com/yau/digitalrmb/platformintegration/infrastructure/JdbcPlatformIdentityRepository.java +++ b/src/main/java/com/yau/digitalrmb/platformintegration/infrastructure/JdbcPlatformIdentityRepository.java @@ -4,6 +4,7 @@ import com.yau.digitalrmb.platformintegration.application.PlatformIdentityReposi import com.yau.digitalrmb.platformintegration.domain.PlatformActor; import com.yau.digitalrmb.platformintegration.domain.PlatformRole; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -65,6 +66,9 @@ public class JdbcPlatformIdentityRepository implements PlatformIdentityRepositor private Optional query(String sql, Object value, PlatformRole role) { List actors = jdbcTemplate.query(sql, new MapSqlParameterSource("value", value), rowMapper(role)); + if (actors.size() > 1) { + throw new IncorrectResultSizeDataAccessException(1, actors.size()); + } return actors.isEmpty() ? Optional.empty() : Optional.of(actors.get(0)); } diff --git a/src/test/java/com/yau/digitalrmb/platformintegration/application/CasTicketValidatorTest.java b/src/test/java/com/yau/digitalrmb/platformintegration/application/CasTicketValidatorTest.java index 82c77bb..b5d010b 100644 --- a/src/test/java/com/yau/digitalrmb/platformintegration/application/CasTicketValidatorTest.java +++ b/src/test/java/com/yau/digitalrmb/platformintegration/application/CasTicketValidatorTest.java @@ -1,12 +1,32 @@ package com.yau.digitalrmb.platformintegration.application; +import com.yau.digitalrmb.platformintegration.config.PlatformIntegrationProperties; import com.yau.digitalrmb.shared.exception.BusinessException; import org.junit.jupiter.api.Test; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.client.RestTemplate; +import org.springframework.http.client.SimpleClientHttpRequestFactory; + +import java.lang.reflect.Field; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; class CasTicketValidatorTest { + @Test + void configuresFiveSecondTimeoutsForCasRequests() throws Exception { + PlatformIntegrationProperties properties = new PlatformIntegrationProperties(); + CasTicketValidator validator = new CasTicketValidator(properties); + Field restTemplateField = ReflectionUtils.findField(CasTicketValidator.class, "restTemplate"); + ReflectionUtils.makeAccessible(restTemplateField); + RestTemplate restTemplate = (RestTemplate) ReflectionUtils.getField(restTemplateField, validator); + + assertThat(restTemplate.getRequestFactory()).isInstanceOf(SimpleClientHttpRequestFactory.class); + SimpleClientHttpRequestFactory requestFactory = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory(); + assertThat(readIntField(requestFactory, "connectTimeout")).isEqualTo(5000); + assertThat(readIntField(requestFactory, "readTimeout")).isEqualTo(5000); + } + @Test void parsesSuccessfulCasAccountAndRejectsExternalEntityPayloads() { String success = "" @@ -18,4 +38,10 @@ class CasTicketValidatorTest { assertThat(CasTicketValidator.parseAccount(success)).isEqualTo("t001"); assertThatThrownBy(() -> CasTicketValidator.parseAccount(xxe)).isInstanceOf(BusinessException.class); } + + private int readIntField(Object target, String fieldName) throws Exception { + Field field = ReflectionUtils.findField(target.getClass(), fieldName); + ReflectionUtils.makeAccessible(field); + return (Integer) ReflectionUtils.getField(field, target); + } } diff --git a/src/test/java/com/yau/digitalrmb/platformintegration/infrastructure/JdbcPlatformIdentityRepositoryTest.java b/src/test/java/com/yau/digitalrmb/platformintegration/infrastructure/JdbcPlatformIdentityRepositoryTest.java index 0656037..4c5b0bc 100644 --- a/src/test/java/com/yau/digitalrmb/platformintegration/infrastructure/JdbcPlatformIdentityRepositoryTest.java +++ b/src/test/java/com/yau/digitalrmb/platformintegration/infrastructure/JdbcPlatformIdentityRepositoryTest.java @@ -6,6 +6,7 @@ import com.yau.digitalrmb.platformintegration.domain.PlatformRole; import org.h2.jdbcx.JdbcDataSource; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import javax.sql.DataSource; @@ -14,6 +15,7 @@ import java.sql.Statement; import java.time.Instant; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; class JdbcPlatformIdentityRepositoryTest { private DataSource dataSource; @@ -54,6 +56,17 @@ class JdbcPlatformIdentityRepositoryTest { assertThat(repository.findByPlatformUserId(202L)).isEmpty(); } + @Test + void rejectsDuplicateSchoolAccounts() throws Exception { + execute("INSERT INTO core_user(ID, CODE, NAME, STATE, JOB_TYPE1, DEL_FLAG) VALUES (301, 'duplicate', 'student one', 'S1', 'JT_S_03', 0)"); + execute("INSERT INTO core_user(ID, CODE, NAME, STATE, JOB_TYPE1, DEL_FLAG) VALUES (302, 'duplicate', 'student two', 'S1', 'JT_S_03', 0)"); + execute("INSERT INTO student(student_id, user_id, student_status, add_time) VALUES (11, 301, 1, '2026-01-01 00:00:00')"); + execute("INSERT INTO student(student_id, user_id, student_status, add_time) VALUES (12, 302, 1, '2026-01-01 00:00:00')"); + + assertThatThrownBy(() -> repository.findBySchoolAccount("duplicate")) + .isInstanceOf(IncorrectResultSizeDataAccessException.class); + } + @Test private void createSchema() throws Exception { execute("CREATE TABLE core_user(ID BIGINT PRIMARY KEY, CODE VARCHAR(64), NAME VARCHAR(64), STATE VARCHAR(16), JOB_TYPE1 VARCHAR(16), DEL_FLAG INT)");