feat(apitest): 新增 API 测试环境与安全配置
- 新增 apitest 专用 MySQL 配置与 Docker 编排(docker/apitest-mysql.yml、docker/apitest-mysql/) - 增加 ApiTestSecurityConfig / ApiTestSecurityProperties 与 ApiTestAuthenticationFilter - 新增 application-apitest.yml 与相关测试目录(play-admin/src/test/java/com/starry/admin/api/) - 调整根 pom 与 play-admin/pom 依赖,优化 SpringSecurityConfig 以兼容 apitest
This commit is contained in:
27
docker/apitest-mysql.yml
Normal file
27
docker/apitest-mysql.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql-apitest:
|
||||||
|
image: mysql:8.0.32
|
||||||
|
container_name: peipei-mysql-apitest
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: root
|
||||||
|
MYSQL_DATABASE: peipei_apitest
|
||||||
|
MYSQL_USER: apitest
|
||||||
|
MYSQL_PASSWORD: apitest
|
||||||
|
ports:
|
||||||
|
- "33306:3306"
|
||||||
|
volumes:
|
||||||
|
- ./apitest-mysql/init:/docker-entrypoint-initdb.d:ro
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-proot"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
command:
|
||||||
|
- "--default-authentication-plugin=mysql_native_password"
|
||||||
|
- "--lower_case_table_names=1"
|
||||||
|
- "--explicit_defaults_for_timestamp=1"
|
||||||
|
- "--character-set-server=utf8mb4"
|
||||||
|
- "--collation-server=utf8mb4_unicode_ci"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
GRANT SELECT ON performance_schema.* TO 'apitest'@'%';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
10
docker/apitest-mysql/init/README.md
Normal file
10
docker/apitest-mysql/init/README.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# API Test MySQL Seed Files
|
||||||
|
|
||||||
|
将初始化 schema 和种子数据的 SQL 文件放在此目录下,文件会在 `mysql-apitest` 容器启动时自动执行。
|
||||||
|
|
||||||
|
推荐约定:
|
||||||
|
- `000-schema.sql`:创建数据库/表结构(可复用 Flyway 生成的整库脚本)。
|
||||||
|
- `100-seed-*.sql`:插入基础租户、用户、商品、优惠券等测试数据。
|
||||||
|
- `900-cleanup.sql`:可选的清理脚本,用于重置状态。
|
||||||
|
|
||||||
|
容器销毁(`docker-compose down -v`)后数据会一起删除,保证每次测试环境一致。
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
<maven.compiler.source>11</maven.compiler.source>
|
<maven.compiler.source>11</maven.compiler.source>
|
||||||
<maven.compiler.target>11</maven.compiler.target>
|
<maven.compiler.target>11</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<spring.profiles.active>test</spring.profiles.active>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -173,6 +174,22 @@
|
|||||||
<artifactId>json-path</artifactId>
|
<artifactId>json-path</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.junit.vintage</groupId>
|
||||||
|
<artifactId>junit-vintage-engine</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
@@ -203,6 +220,9 @@
|
|||||||
<version>3.0.0-M7</version>
|
<version>3.0.0-M7</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<useSystemClassLoader>false</useSystemClassLoader>
|
<useSystemClassLoader>false</useSystemClassLoader>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<spring.profiles.active>${spring.profiles.active}</spring.profiles.active>
|
||||||
|
</systemPropertyVariables>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|||||||
@@ -0,0 +1,318 @@
|
|||||||
|
package com.starry.admin.common.apitest;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkCommodityEntity;
|
||||||
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
|
||||||
|
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||||
|
import com.starry.admin.modules.clerk.service.IPlayClerkCommodityService;
|
||||||
|
import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService;
|
||||||
|
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||||
|
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||||
|
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||||
|
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
|
||||||
|
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
|
||||||
|
import com.starry.admin.modules.shop.module.entity.PlayCommodityInfoEntity;
|
||||||
|
import com.starry.admin.modules.shop.service.IPlayCommodityInfoService;
|
||||||
|
import com.starry.admin.modules.system.module.entity.SysTenantEntity;
|
||||||
|
import com.starry.admin.modules.system.module.entity.SysTenantPackageEntity;
|
||||||
|
import com.starry.admin.modules.system.module.entity.SysUserEntity;
|
||||||
|
import com.starry.admin.modules.system.service.ISysTenantPackageService;
|
||||||
|
import com.starry.admin.modules.system.service.ISysTenantService;
|
||||||
|
import com.starry.admin.modules.system.service.SysUserService;
|
||||||
|
import com.starry.admin.utils.SecurityUtils;
|
||||||
|
import com.starry.common.context.CustomSecurityContextHolder;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Profile("apitest")
|
||||||
|
public class ApiTestDataSeeder implements CommandLineRunner {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ApiTestDataSeeder.class);
|
||||||
|
|
||||||
|
public static final String DEFAULT_PACKAGE_ID = "pkg-basic";
|
||||||
|
public static final String DEFAULT_TENANT_ID = "tenant-apitest";
|
||||||
|
public static final String DEFAULT_TENANT_KEY = "tenant-key-apitest";
|
||||||
|
public static final String DEFAULT_TENANT_NAME = "API Test Tenant";
|
||||||
|
public static final String DEFAULT_ADMIN_USER_ID = "user-apitest-admin";
|
||||||
|
public static final String DEFAULT_ADMIN_USERNAME = "apitest-admin";
|
||||||
|
public static final String DEFAULT_GROUP_ID = "group-basic";
|
||||||
|
public static final String DEFAULT_CLERK_LEVEL_ID = "lvl-basic";
|
||||||
|
public static final String DEFAULT_CLERK_ID = "clerk-apitest";
|
||||||
|
public static final String DEFAULT_CLERK_OPEN_ID = "openid-clerk-apitest";
|
||||||
|
public static final String DEFAULT_COMMODITY_ID = "svc-basic";
|
||||||
|
public static final String DEFAULT_CLERK_COMMODITY_ID = "clerk-svc-basic";
|
||||||
|
public static final String DEFAULT_CUSTOMER_ID = "customer-apitest";
|
||||||
|
|
||||||
|
private final ISysTenantPackageService tenantPackageService;
|
||||||
|
private final ISysTenantService tenantService;
|
||||||
|
private final SysUserService sysUserService;
|
||||||
|
private final IPlayPersonnelGroupInfoService personnelGroupInfoService;
|
||||||
|
private final IPlayClerkLevelInfoService clerkLevelInfoService;
|
||||||
|
private final IPlayClerkUserInfoService clerkUserInfoService;
|
||||||
|
private final IPlayCommodityInfoService commodityInfoService;
|
||||||
|
private final IPlayClerkCommodityService clerkCommodityService;
|
||||||
|
private final IPlayCustomUserInfoService customUserInfoService;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
public ApiTestDataSeeder(
|
||||||
|
ISysTenantPackageService tenantPackageService,
|
||||||
|
ISysTenantService tenantService,
|
||||||
|
SysUserService sysUserService,
|
||||||
|
IPlayPersonnelGroupInfoService personnelGroupInfoService,
|
||||||
|
IPlayClerkLevelInfoService clerkLevelInfoService,
|
||||||
|
IPlayClerkUserInfoService clerkUserInfoService,
|
||||||
|
IPlayCommodityInfoService commodityInfoService,
|
||||||
|
IPlayClerkCommodityService clerkCommodityService,
|
||||||
|
IPlayCustomUserInfoService customUserInfoService,
|
||||||
|
PasswordEncoder passwordEncoder) {
|
||||||
|
this.tenantPackageService = tenantPackageService;
|
||||||
|
this.tenantService = tenantService;
|
||||||
|
this.sysUserService = sysUserService;
|
||||||
|
this.personnelGroupInfoService = personnelGroupInfoService;
|
||||||
|
this.clerkLevelInfoService = clerkLevelInfoService;
|
||||||
|
this.clerkUserInfoService = clerkUserInfoService;
|
||||||
|
this.commodityInfoService = commodityInfoService;
|
||||||
|
this.clerkCommodityService = clerkCommodityService;
|
||||||
|
this.customUserInfoService = customUserInfoService;
|
||||||
|
this.passwordEncoder = passwordEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void run(String... args) {
|
||||||
|
seedTenantPackage();
|
||||||
|
seedTenant();
|
||||||
|
|
||||||
|
String originalTenant = SecurityUtils.getTenantId();
|
||||||
|
try {
|
||||||
|
SecurityUtils.setTenantId(DEFAULT_TENANT_ID);
|
||||||
|
seedAdminUser();
|
||||||
|
seedPersonnelGroup();
|
||||||
|
seedClerkLevel();
|
||||||
|
seedCommodity();
|
||||||
|
seedClerk();
|
||||||
|
seedClerkCommodity();
|
||||||
|
seedCustomer();
|
||||||
|
} finally {
|
||||||
|
if (Objects.nonNull(originalTenant)) {
|
||||||
|
SecurityUtils.setTenantId(originalTenant);
|
||||||
|
}
|
||||||
|
CustomSecurityContextHolder.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedTenantPackage() {
|
||||||
|
long existing = tenantPackageService.count(Wrappers.<SysTenantPackageEntity>lambdaQuery()
|
||||||
|
.eq(SysTenantPackageEntity::getPackageId, DEFAULT_PACKAGE_ID));
|
||||||
|
if (existing > 0) {
|
||||||
|
log.info("API test tenant package {} already exists", DEFAULT_PACKAGE_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SysTenantPackageEntity entity = new SysTenantPackageEntity();
|
||||||
|
entity.setPackageId(DEFAULT_PACKAGE_ID);
|
||||||
|
entity.setPackageName("API测试基础套餐");
|
||||||
|
entity.setStatus("0");
|
||||||
|
entity.setMenuIds("[]");
|
||||||
|
entity.setRemarks("Seeded for API integration tests");
|
||||||
|
tenantPackageService.save(entity);
|
||||||
|
log.info("Inserted API test tenant package {}", DEFAULT_PACKAGE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedTenant() {
|
||||||
|
SysTenantEntity tenant = tenantService.getById(DEFAULT_TENANT_ID);
|
||||||
|
if (tenant != null) {
|
||||||
|
log.info("API test tenant {} already exists", DEFAULT_TENANT_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SysTenantEntity entity = new SysTenantEntity();
|
||||||
|
entity.setTenantId(DEFAULT_TENANT_ID);
|
||||||
|
entity.setTenantName(DEFAULT_TENANT_NAME);
|
||||||
|
entity.setTenantType("0");
|
||||||
|
entity.setTenantStatus("0");
|
||||||
|
entity.setTenantCode("apitest");
|
||||||
|
entity.setTenantKey(DEFAULT_TENANT_KEY);
|
||||||
|
entity.setPackageId(DEFAULT_PACKAGE_ID);
|
||||||
|
entity.setTenantTime(new Date(System.currentTimeMillis() + 365L * 24 * 3600 * 1000));
|
||||||
|
entity.setUserName(DEFAULT_ADMIN_USERNAME);
|
||||||
|
entity.setUserPwd(passwordEncoder.encode("apitest-secret"));
|
||||||
|
entity.setPhone("13800000000");
|
||||||
|
entity.setEmail("apitest@example.com");
|
||||||
|
entity.setAddress("API Test Street 1");
|
||||||
|
tenantService.save(entity);
|
||||||
|
log.info("Inserted API test tenant {}", DEFAULT_TENANT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedAdminUser() {
|
||||||
|
SysUserEntity existing = sysUserService.getById(DEFAULT_ADMIN_USER_ID);
|
||||||
|
if (existing != null) {
|
||||||
|
log.info("API test admin user {} already exists", DEFAULT_ADMIN_USER_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SysUserEntity admin = new SysUserEntity();
|
||||||
|
admin.setUserId(DEFAULT_ADMIN_USER_ID);
|
||||||
|
admin.setUserCode(DEFAULT_ADMIN_USERNAME);
|
||||||
|
admin.setPassWord(passwordEncoder.encode("apitest-secret"));
|
||||||
|
admin.setRealName("API Test Admin");
|
||||||
|
admin.setUserNickname("API Admin");
|
||||||
|
admin.setStatus(0);
|
||||||
|
admin.setUserType(1);
|
||||||
|
admin.setTenantId(DEFAULT_TENANT_ID);
|
||||||
|
admin.setMobile("13800000000");
|
||||||
|
admin.setAddTime(LocalDateTime.now());
|
||||||
|
admin.setSuperAdmin(Boolean.TRUE);
|
||||||
|
sysUserService.save(admin);
|
||||||
|
log.info("Inserted API test admin user {}", DEFAULT_ADMIN_USER_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedPersonnelGroup() {
|
||||||
|
PlayPersonnelGroupInfoEntity group = personnelGroupInfoService.getById(DEFAULT_GROUP_ID);
|
||||||
|
if (group != null) {
|
||||||
|
log.info("API test personnel group {} already exists", DEFAULT_GROUP_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayPersonnelGroupInfoEntity entity = new PlayPersonnelGroupInfoEntity();
|
||||||
|
entity.setId(DEFAULT_GROUP_ID);
|
||||||
|
entity.setTenantId(DEFAULT_TENANT_ID);
|
||||||
|
entity.setSysUserId(DEFAULT_ADMIN_USER_ID);
|
||||||
|
entity.setSysUserCode(DEFAULT_ADMIN_USERNAME);
|
||||||
|
entity.setGroupName("测试小组");
|
||||||
|
entity.setLeaderName("API Admin");
|
||||||
|
entity.setAddTime(LocalDateTime.now());
|
||||||
|
personnelGroupInfoService.save(entity);
|
||||||
|
log.info("Inserted API test personnel group {}", DEFAULT_GROUP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedClerkLevel() {
|
||||||
|
PlayClerkLevelInfoEntity level = clerkLevelInfoService.getById(DEFAULT_CLERK_LEVEL_ID);
|
||||||
|
if (level != null) {
|
||||||
|
log.info("API test clerk level {} already exists", DEFAULT_CLERK_LEVEL_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayClerkLevelInfoEntity entity = new PlayClerkLevelInfoEntity();
|
||||||
|
entity.setId(DEFAULT_CLERK_LEVEL_ID);
|
||||||
|
entity.setTenantId(DEFAULT_TENANT_ID);
|
||||||
|
entity.setName("基础等级");
|
||||||
|
entity.setLevel(1);
|
||||||
|
entity.setFirstRegularRatio(60);
|
||||||
|
entity.setNotFirstRegularRatio(50);
|
||||||
|
entity.setFirstRandomRadio(55);
|
||||||
|
entity.setNotFirstRandomRadio(45);
|
||||||
|
entity.setFirstRewardRatio(40);
|
||||||
|
entity.setNotFirstRewardRatio(35);
|
||||||
|
clerkLevelInfoService.save(entity);
|
||||||
|
log.info("Inserted API test clerk level {}", DEFAULT_CLERK_LEVEL_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedCommodity() {
|
||||||
|
PlayCommodityInfoEntity commodity = commodityInfoService.getById(DEFAULT_COMMODITY_ID);
|
||||||
|
if (commodity != null) {
|
||||||
|
log.info("API test commodity {} already exists", DEFAULT_COMMODITY_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayCommodityInfoEntity entity = new PlayCommodityInfoEntity();
|
||||||
|
entity.setId(DEFAULT_COMMODITY_ID);
|
||||||
|
entity.setTenantId(DEFAULT_TENANT_ID);
|
||||||
|
entity.setItemType("service");
|
||||||
|
entity.setItemName("60分钟语音陪聊");
|
||||||
|
entity.setServiceDuration("60min");
|
||||||
|
entity.setEnableStace("1");
|
||||||
|
entity.setSort(1);
|
||||||
|
commodityInfoService.save(entity);
|
||||||
|
log.info("Inserted API test commodity {}", DEFAULT_COMMODITY_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedClerk() {
|
||||||
|
PlayClerkUserInfoEntity clerk = clerkUserInfoService.getById(DEFAULT_CLERK_ID);
|
||||||
|
if (clerk != null) {
|
||||||
|
log.info("API test clerk {} already exists", DEFAULT_CLERK_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayClerkUserInfoEntity entity = new PlayClerkUserInfoEntity();
|
||||||
|
entity.setId(DEFAULT_CLERK_ID);
|
||||||
|
entity.setTenantId(DEFAULT_TENANT_ID);
|
||||||
|
entity.setSysUserId(DEFAULT_ADMIN_USER_ID);
|
||||||
|
entity.setOpenid(DEFAULT_CLERK_OPEN_ID);
|
||||||
|
entity.setNickname("小测官");
|
||||||
|
entity.setGroupId(DEFAULT_GROUP_ID);
|
||||||
|
entity.setLevelId(DEFAULT_CLERK_LEVEL_ID);
|
||||||
|
entity.setFixingLevel("1");
|
||||||
|
entity.setSex("2");
|
||||||
|
entity.setPhone("13900000001");
|
||||||
|
entity.setWeiChatCode("apitest-clerk");
|
||||||
|
entity.setAvatar("https://example.com/avatar.png");
|
||||||
|
entity.setAccountBalance(BigDecimal.ZERO);
|
||||||
|
entity.setOnboardingState("1");
|
||||||
|
entity.setListingState("1");
|
||||||
|
entity.setDisplayState("1");
|
||||||
|
entity.setRandomOrderState("1");
|
||||||
|
entity.setClerkState("1");
|
||||||
|
entity.setEntryTime(LocalDateTime.now());
|
||||||
|
clerkUserInfoService.save(entity);
|
||||||
|
log.info("Inserted API test clerk {}", DEFAULT_CLERK_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedClerkCommodity() {
|
||||||
|
PlayClerkCommodityEntity mapping = clerkCommodityService.getById(DEFAULT_CLERK_COMMODITY_ID);
|
||||||
|
if (mapping != null) {
|
||||||
|
log.info("API test clerk commodity {} already exists", DEFAULT_CLERK_COMMODITY_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayClerkCommodityEntity entity = new PlayClerkCommodityEntity();
|
||||||
|
entity.setId(DEFAULT_CLERK_COMMODITY_ID);
|
||||||
|
entity.setTenantId(DEFAULT_TENANT_ID);
|
||||||
|
entity.setClerkId(DEFAULT_CLERK_ID);
|
||||||
|
entity.setCommodityId(DEFAULT_COMMODITY_ID);
|
||||||
|
entity.setCommodityName("60分钟语音陪聊");
|
||||||
|
entity.setEnablingState("1");
|
||||||
|
entity.setSort(1);
|
||||||
|
clerkCommodityService.save(entity);
|
||||||
|
log.info("Inserted API test clerk commodity link {}", DEFAULT_CLERK_COMMODITY_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seedCustomer() {
|
||||||
|
PlayCustomUserInfoEntity customer = customUserInfoService.getById(DEFAULT_CUSTOMER_ID);
|
||||||
|
if (customer != null) {
|
||||||
|
log.info("API test customer {} already exists", DEFAULT_CUSTOMER_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayCustomUserInfoEntity entity = new PlayCustomUserInfoEntity();
|
||||||
|
entity.setId(DEFAULT_CUSTOMER_ID);
|
||||||
|
entity.setTenantId(DEFAULT_TENANT_ID);
|
||||||
|
entity.setOpenid("openid-customer-apitest");
|
||||||
|
entity.setUnionid("unionid-customer-apitest");
|
||||||
|
entity.setNickname("测试顾客");
|
||||||
|
entity.setSex(1);
|
||||||
|
entity.setPhone("13700000002");
|
||||||
|
entity.setWeiChatCode("apitest-customer");
|
||||||
|
entity.setAccountBalance(new BigDecimal("200.00"));
|
||||||
|
entity.setAccumulatedRechargeAmount(new BigDecimal("200.00"));
|
||||||
|
entity.setAccumulatedConsumptionAmount(BigDecimal.ZERO);
|
||||||
|
entity.setAccountState("1");
|
||||||
|
entity.setSubscribeState("1");
|
||||||
|
entity.setPurchaseState("1");
|
||||||
|
entity.setMobilePhoneState("1");
|
||||||
|
entity.setRegistrationTime(new Date());
|
||||||
|
entity.setLastLoginTime(new Date());
|
||||||
|
customUserInfoService.save(entity);
|
||||||
|
log.info("Inserted API test customer {}", DEFAULT_CUSTOMER_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.starry.admin.common.security.config;
|
||||||
|
|
||||||
|
import com.starry.admin.common.security.filter.ApiTestAuthenticationFilter;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
|
@Profile("apitest")
|
||||||
|
@EnableConfigurationProperties(ApiTestSecurityProperties.class)
|
||||||
|
public class ApiTestSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
private final ApiTestSecurityProperties properties;
|
||||||
|
|
||||||
|
public ApiTestSecurityConfig(ApiTestSecurityProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ApiTestAuthenticationFilter apiTestAuthenticationFilter() {
|
||||||
|
return new ApiTestAuthenticationFilter(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http.csrf().disable()
|
||||||
|
.formLogin().disable()
|
||||||
|
.logout().disable()
|
||||||
|
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
||||||
|
.authorizeRequests().anyRequest().authenticated().and()
|
||||||
|
.addFilterBefore(apiTestAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.starry.admin.common.security.config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "apitest.security")
|
||||||
|
public class ApiTestSecurityProperties {
|
||||||
|
|
||||||
|
private String tenantHeader = "X-Tenant";
|
||||||
|
private String userHeader = "X-Test-User";
|
||||||
|
private final Defaults defaults = new Defaults();
|
||||||
|
|
||||||
|
public String getTenantHeader() {
|
||||||
|
return tenantHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTenantHeader(String tenantHeader) {
|
||||||
|
this.tenantHeader = tenantHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserHeader() {
|
||||||
|
return userHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserHeader(String userHeader) {
|
||||||
|
this.userHeader = userHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Defaults getDefaults() {
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Defaults {
|
||||||
|
|
||||||
|
private String tenantId = "tenant-apitest";
|
||||||
|
private String userId = "apitest-user";
|
||||||
|
private List<String> roles = new ArrayList<>();
|
||||||
|
private List<String> permissions = new ArrayList<>();
|
||||||
|
|
||||||
|
public String getTenantId() {
|
||||||
|
return tenantId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTenantId(String tenantId) {
|
||||||
|
this.tenantId = tenantId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoles(List<String> roles) {
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPermissions() {
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPermissions(List<String> permissions) {
|
||||||
|
this.permissions = permissions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import java.util.Set;
|
|||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
@@ -31,6 +32,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
|
|||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
|
@Profile("!apitest")
|
||||||
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
|
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package com.starry.admin.common.security.filter;
|
||||||
|
|
||||||
|
import com.starry.admin.common.domain.LoginUser;
|
||||||
|
import com.starry.admin.common.security.config.ApiTestSecurityProperties;
|
||||||
|
import com.starry.admin.modules.system.module.entity.SysUserEntity;
|
||||||
|
import com.starry.common.constant.SecurityConstants;
|
||||||
|
import com.starry.common.context.CustomSecurityContextHolder;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
public class ApiTestAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final ApiTestSecurityProperties properties;
|
||||||
|
|
||||||
|
public ApiTestAuthenticationFilter(ApiTestSecurityProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
String requestedUser = request.getHeader(properties.getUserHeader());
|
||||||
|
String requestedTenant = request.getHeader(properties.getTenantHeader());
|
||||||
|
|
||||||
|
String userId = StringUtils.hasText(requestedUser) ? requestedUser : properties.getDefaults().getUserId();
|
||||||
|
String tenantId = StringUtils.hasText(requestedTenant) ? requestedTenant : properties.getDefaults().getTenantId();
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(userId) || !StringUtils.hasText(tenantId)) {
|
||||||
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
|
response.setContentType("application/json");
|
||||||
|
response.getWriter().write("{\"code\":401,\"message\":\"Missing test user or tenant header\"}");
|
||||||
|
response.getWriter().flush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
LoginUser loginUser = buildLoginUser(userId, tenantId);
|
||||||
|
|
||||||
|
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null,
|
||||||
|
Collections.emptyList());
|
||||||
|
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
CustomSecurityContextHolder.set(SecurityConstants.DETAILS_USER_ID, userId);
|
||||||
|
CustomSecurityContextHolder.set(SecurityConstants.DETAILS_USERNAME, userId);
|
||||||
|
CustomSecurityContextHolder.setTenantId(tenantId);
|
||||||
|
CustomSecurityContextHolder.setPermission(String.join(",", loginUser.getPermissions()));
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
} finally {
|
||||||
|
CustomSecurityContextHolder.remove();
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoginUser buildLoginUser(String userId, String tenantId) {
|
||||||
|
SysUserEntity sysUser = new SysUserEntity();
|
||||||
|
sysUser.setUserId(userId);
|
||||||
|
sysUser.setUserCode(userId);
|
||||||
|
sysUser.setRealName(userId);
|
||||||
|
sysUser.setTenantId(tenantId);
|
||||||
|
sysUser.setSuperAdmin(Boolean.FALSE);
|
||||||
|
sysUser.setStatus(0);
|
||||||
|
|
||||||
|
LoginUser loginUser = new LoginUser();
|
||||||
|
loginUser.setUser(sysUser);
|
||||||
|
loginUser.setUserId(userId);
|
||||||
|
loginUser.setUserName(userId);
|
||||||
|
loginUser.setToken("apitest-" + userId + "-" + tenantId);
|
||||||
|
loginUser.setLoginTime(System.currentTimeMillis());
|
||||||
|
loginUser.setExpireTime(System.currentTimeMillis() + 3600_000);
|
||||||
|
loginUser.setTenantEndDate(new Date(System.currentTimeMillis() + 3600_000));
|
||||||
|
loginUser.setTenantStatus(0);
|
||||||
|
|
||||||
|
Set<String> roles = new HashSet<>(properties.getDefaults().getRoles());
|
||||||
|
Set<String> permissions = new HashSet<>(properties.getDefaults().getPermissions());
|
||||||
|
loginUser.setRoles(roles);
|
||||||
|
loginUser.setPermissions(permissions);
|
||||||
|
loginUser.setCurrentRole(roles.stream().findFirst().orElse(null));
|
||||||
|
|
||||||
|
return loginUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
82
play-admin/src/main/resources/application-apitest.yml
Normal file
82
play-admin/src/main/resources/application-apitest.yml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: admin-tenant-apitest
|
||||||
|
flyway:
|
||||||
|
table: admin_flyway_schema_history
|
||||||
|
baseline-on-migrate: true
|
||||||
|
baseline-version: 1
|
||||||
|
enabled: true
|
||||||
|
locations: classpath:db/migration
|
||||||
|
clean-disabled: false
|
||||||
|
validate-on-migrate: false
|
||||||
|
out-of-order: false
|
||||||
|
datasource:
|
||||||
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
|
driverClassName: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://localhost:33306/peipei_apitest?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true
|
||||||
|
username: apitest
|
||||||
|
password: apitest
|
||||||
|
druid:
|
||||||
|
enable: true
|
||||||
|
db-type: mysql
|
||||||
|
filters: stat,wall
|
||||||
|
max-active: 20
|
||||||
|
initial-size: 1
|
||||||
|
max-wait: 60000
|
||||||
|
min-idle: 1
|
||||||
|
time-between-eviction-runs-millis: 60000
|
||||||
|
min-evictable-idle-time-millis: 300000
|
||||||
|
validation-query: select 'x'
|
||||||
|
test-while-idle: true
|
||||||
|
test-on-borrow: false
|
||||||
|
test-on-return: false
|
||||||
|
pool-prepared-statements: true
|
||||||
|
max-open-prepared-statements: 20
|
||||||
|
web-stat-filter:
|
||||||
|
enabled: false
|
||||||
|
stat-view-servlet:
|
||||||
|
enabled: false
|
||||||
|
redis:
|
||||||
|
host: 127.0.0.1
|
||||||
|
database: 0
|
||||||
|
port: 36379
|
||||||
|
password:
|
||||||
|
timeout: 3000ms
|
||||||
|
task:
|
||||||
|
scheduling:
|
||||||
|
enabled: false
|
||||||
|
execution:
|
||||||
|
shutdown:
|
||||||
|
await-termination: true
|
||||||
|
await-termination-period: 5s
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
root: info
|
||||||
|
com.starry: debug
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
tokenHeader: X-Test-Token
|
||||||
|
tokenHead: Bearer
|
||||||
|
secret: apitest-secret
|
||||||
|
expiration: 360000
|
||||||
|
|
||||||
|
token:
|
||||||
|
header: Authorization
|
||||||
|
secret: apitest-override-secret
|
||||||
|
expireTime: 60
|
||||||
|
|
||||||
|
xl:
|
||||||
|
login:
|
||||||
|
authCode:
|
||||||
|
enable: false
|
||||||
|
|
||||||
|
apitest:
|
||||||
|
security:
|
||||||
|
tenant-header: X-Tenant
|
||||||
|
user-header: X-Test-User
|
||||||
|
defaults:
|
||||||
|
tenant-id: tenant-apitest
|
||||||
|
user-id: apitest-user
|
||||||
|
roles:
|
||||||
|
- ROLE_TESTER
|
||||||
|
permissions: []
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.starry.admin.api;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
@ActiveProfiles("apitest")
|
||||||
|
public abstract class AbstractApiTest {
|
||||||
|
|
||||||
|
protected static final String TENANT_HEADER = "X-Tenant";
|
||||||
|
protected static final String USER_HEADER = "X-Test-User";
|
||||||
|
protected static final String DEFAULT_TENANT = "tenant-apitest";
|
||||||
|
protected static final String DEFAULT_USER = "apitest-user";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected MockMvc mockMvc;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.starry.admin.api;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class HealthControllerApiTest extends AbstractApiTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pingReturnsPong() throws Exception {
|
||||||
|
mockMvc.perform(get("/health/ping")
|
||||||
|
.header(USER_HEADER, DEFAULT_USER)
|
||||||
|
.header(TENANT_HEADER, DEFAULT_TENANT))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.code").value(200))
|
||||||
|
.andExpect(jsonPath("$.data").value("pong"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.starry.admin.api;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.hasItem;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class SysTenantPackageControllerApiTest extends AbstractApiTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getSimpleListReturnsSeededPackage() throws Exception {
|
||||||
|
mockMvc.perform(get("/platform/package/get-simple-list")
|
||||||
|
.header(USER_HEADER, DEFAULT_USER)
|
||||||
|
.header(TENANT_HEADER, DEFAULT_TENANT))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.code").value(200))
|
||||||
|
.andExpect(jsonPath("$.data").isArray())
|
||||||
|
.andExpect(jsonPath("$.data[*].id", hasItem("pkg-basic")));
|
||||||
|
}
|
||||||
|
}
|
||||||
8
pom.xml
8
pom.xml
@@ -333,6 +333,14 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>apitest</id>
|
||||||
|
<properties>
|
||||||
|
<spring.profiles.active>apitest</spring.profiles.active>
|
||||||
|
<apitest.mysql.port>33306</apitest.mysql.port>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
|
||||||
<!-- macOS Apple Silicon -->
|
<!-- macOS Apple Silicon -->
|
||||||
<profile>
|
<profile>
|
||||||
<id>osx-arm64</id>
|
<id>osx-arm64</id>
|
||||||
|
|||||||
Reference in New Issue
Block a user