fix: default register client id
This commit is contained in:
19
openspec/changes/fix-register-body-validation/.comet.yaml
Normal file
19
openspec/changes/fix-register-body-validation/.comet.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
workflow: hotfix
|
||||||
|
phase: build
|
||||||
|
context_compression: off
|
||||||
|
build_mode: direct
|
||||||
|
build_pause: null
|
||||||
|
subagent_dispatch: null
|
||||||
|
tdd_mode: direct
|
||||||
|
isolation: branch
|
||||||
|
verify_mode: light
|
||||||
|
auto_transition: true
|
||||||
|
base_ref: 2f7597ed53736b965191d4e48ee2c73d09ff71c0
|
||||||
|
design_doc: null
|
||||||
|
plan: null
|
||||||
|
verify_result: pending
|
||||||
|
verification_report: null
|
||||||
|
branch_status: pending
|
||||||
|
created_at: 2026-08-14
|
||||||
|
verified_at: null
|
||||||
|
archived: false
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-08-14
|
||||||
27
openspec/changes/fix-register-body-validation/design.md
Normal file
27
openspec/changes/fix-register-body-validation/design.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
## Context
|
||||||
|
|
||||||
|
`AuthController.register` 当前依赖 Spring MVC 的 `@Validated` 自动校验。该校验发生在控制器方法调用之前,所以方法体无法为缺失的 `clientId` 补默认值。
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:**
|
||||||
|
|
||||||
|
- 空白 `clientId` 使用固定注册客户端 ID `32324a04c0175c8d61ac8a282553aea1`。
|
||||||
|
- 默认值补充后仍执行 `RegisterBody` 的全部 Bean Validation 约束。
|
||||||
|
- 保持注册开关检查和注册服务调用顺序不变。
|
||||||
|
|
||||||
|
**Non-Goals:**
|
||||||
|
|
||||||
|
- 不调整 `RegisterBody` 的继承结构或其他登录字段。
|
||||||
|
- 不改变登录接口和认证客户端配置。
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- 移除注册方法参数上的 `@Validated`,在方法体开始处补默认 `clientId` 后调用现有 `ValidatorUtils.validate(user)`。该工具已被认证控制器和各登录策略使用,异常处理行为与现有认证流程一致。
|
||||||
|
- 使用 `StringUtils.isBlank`,同时覆盖 `null`、空字符串和纯空白字符串。
|
||||||
|
- 通过 `AuthController` 单元测试观察注册服务收到的请求对象,验证公共接口行为。
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- 固定客户端 ID 变更时需要同步修改代码和测试;本次按明确业务值实现。
|
||||||
|
- 手动校验依赖控制器方法内调用顺序;测试覆盖默认值补充发生在校验和业务调用之前。
|
||||||
25
openspec/changes/fix-register-body-validation/proposal.md
Normal file
25
openspec/changes/fix-register-body-validation/proposal.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
## Why
|
||||||
|
|
||||||
|
`/auth/register` 使用 `@Validated` 在进入控制器方法前校验 `RegisterBody`,缺少 `clientId` 的请求会直接返回“认证客户端id不能为空”,因此控制器无法为注册请求补充系统默认客户端 ID。注册入口需要先补默认值,再执行原有参数校验。
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- 注册接口收到空白 `clientId` 时,将其设置为 `32324a04c0175c8d61ac8a282553aea1`。
|
||||||
|
- 默认值补充完成后,通过现有 `ValidatorUtils` 执行 `RegisterBody` 的完整 Bean Validation。
|
||||||
|
- 增加控制器回归测试,覆盖默认客户端 ID 和注册服务调用。
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
- `auth-registration`: 为已有注册入口补充默认认证客户端 ID 的行为规范。
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
无。仓库当前没有注册流程的既有 OpenSpec。
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
- 影响 `water-admin` 中的 `/auth/register` 控制器及其单元测试。
|
||||||
|
- `/auth/register` 允许调用方省略或传入空白 `clientId`,其他注册参数约束保持不变。
|
||||||
|
- 不新增依赖,不修改数据库结构,也不新增公共端点。
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: 注册请求使用默认认证客户端
|
||||||
|
|
||||||
|
系统 SHALL 在校验注册请求前,为缺失或空白的 `clientId` 设置默认值 `32324a04c0175c8d61ac8a282553aea1`,并在补值后执行 `RegisterBody` 的全部参数约束。
|
||||||
|
|
||||||
|
#### Scenario: 注册请求未提供客户端 ID
|
||||||
|
|
||||||
|
- **WHEN** 调用方提交的注册请求中 `clientId` 缺失或为空白
|
||||||
|
- **THEN** 系统将 `clientId` 设置为 `32324a04c0175c8d61ac8a282553aea1`
|
||||||
|
- **AND** 系统在调用注册服务前校验补值后的注册请求
|
||||||
4
openspec/changes/fix-register-body-validation/tasks.md
Normal file
4
openspec/changes/fix-register-body-validation/tasks.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
## 1. 注册默认客户端 ID
|
||||||
|
|
||||||
|
- [x] 1.1 在控制器单元测试中覆盖空白 `clientId` 自动补默认值的注册场景,并确认修改前失败。
|
||||||
|
- [x] 1.2 调整 `/auth/register` 的校验顺序,补默认 `clientId` 后执行完整参数校验,并运行相关测试。
|
||||||
@@ -201,7 +201,11 @@ public class AuthController {
|
|||||||
*/
|
*/
|
||||||
@ApiEncrypt
|
@ApiEncrypt
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
public R<Void> register(@Validated @RequestBody RegisterBody user) {
|
public R<Void> register(@RequestBody RegisterBody user) {
|
||||||
|
if (StringUtils.isBlank(user.getClientId())) {
|
||||||
|
user.setClientId("32324a04c0175c8d61ac8a282553aea1");
|
||||||
|
}
|
||||||
|
ValidatorUtils.validate(user);
|
||||||
if (!configService.selectRegisterEnabled(user.getTenantId())) {
|
if (!configService.selectRegisterEnabled(user.getTenantId())) {
|
||||||
return R.fail("当前系统没有开启注册功能!");
|
return R.fail("当前系统没有开启注册功能!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
package org.dromara.web.controller;
|
package org.dromara.web.controller;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import jakarta.validation.Validation;
|
||||||
|
import jakarta.validation.ValidatorFactory;
|
||||||
import org.dromara.common.core.domain.R;
|
import org.dromara.common.core.domain.R;
|
||||||
import org.dromara.common.core.domain.model.AccountCancelBody;
|
import org.dromara.common.core.domain.model.AccountCancelBody;
|
||||||
|
import org.dromara.common.core.domain.model.RegisterBody;
|
||||||
import org.dromara.common.satoken.utils.LoginHelper;
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
|
import org.dromara.system.service.ISysConfigService;
|
||||||
import org.dromara.web.service.AccountCancellationService;
|
import org.dromara.web.service.AccountCancellationService;
|
||||||
|
import org.dromara.web.service.SysRegisterService;
|
||||||
import org.junit.jupiter.api.Tag;
|
import org.junit.jupiter.api.Tag;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockedStatic;
|
import org.mockito.MockedStatic;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.context.support.GenericApplicationContext;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.Mockito.mockStatic;
|
import static org.mockito.Mockito.mockStatic;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@Tag("dev")
|
@Tag("dev")
|
||||||
@@ -23,6 +31,12 @@ class AuthControllerUnitTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private AccountCancellationService accountCancellationService;
|
private AccountCancellationService accountCancellationService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SysRegisterService registerService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ISysConfigService configService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void cancelAccount_checksLoginCancelsAccountAndLogsOut() {
|
void cancelAccount_checksLoginCancelsAccountAndLogsOut() {
|
||||||
AuthController controller = new AuthController(
|
AuthController controller = new AuthController(
|
||||||
@@ -52,4 +66,38 @@ class AuthControllerUnitTest {
|
|||||||
stpUtil.verify(() -> StpUtil.logout(100L));
|
stpUtil.verify(() -> StpUtil.logout(100L));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void register_defaultsClientIdWhenMissing() {
|
||||||
|
AuthController controller = new AuthController(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
registerService,
|
||||||
|
configService,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
accountCancellationService
|
||||||
|
);
|
||||||
|
RegisterBody user = new RegisterBody();
|
||||||
|
user.setUsername("register-user");
|
||||||
|
user.setPassword("12345");
|
||||||
|
user.setGrantType("password");
|
||||||
|
when(configService.selectRegisterEnabled(user.getTenantId())).thenReturn(true);
|
||||||
|
|
||||||
|
try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
|
||||||
|
GenericApplicationContext context = new GenericApplicationContext()) {
|
||||||
|
context.getBeanFactory().registerSingleton("validator", validatorFactory.getValidator());
|
||||||
|
context.refresh();
|
||||||
|
new SpringUtil().setApplicationContext(context);
|
||||||
|
|
||||||
|
R<Void> result = controller.register(user);
|
||||||
|
|
||||||
|
assertThat(result.getCode()).isEqualTo(200);
|
||||||
|
assertThat(user.getClientId()).isEqualTo("32324a04c0175c8d61ac8a282553aea1");
|
||||||
|
verify(registerService).register(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user