Browse Source

refactor: RoleController IO to RoleDTO/RoleParam, sink paging into service (ADR-0017 ticket 06)

- rename RoleDetailVO -> RoleDTO extending BaseDTO, add toEntity()
- new RoleParam extends BaseParam; page() now uses PageConverter (size clamp + orderBy anti-injection), fixing pagination bypass
- saveOrUpdate 6 @RequestParam -> RoleDTO; detail returns RoleDTO
- add PageConverterTest covering size clamp + orderBy injection guard
master
luoweijian 4 weeks ago
parent
commit
ef7ad8e368
  1. 30
      .scratch/controller-io-conventions/issues/06-role-controller-io.md
  2. 36
      crm-auth/src/main/java/com/crm/auth/controller/RoleController.java
  3. 69
      crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDTO.java
  4. 60
      crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDetailVO.java
  5. 15
      crm-auth/src/main/java/com/crm/auth/domain/param/RoleParam.java
  6. 16
      crm-auth/src/main/java/com/crm/auth/service/ISysRoleService.java
  7. 19
      crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java
  8. 6
      crm-auth/src/test/java/com/crm/auth/service/impl/SysRoleServiceImplTest.java
  9. 65
      crm-base/src/test/java/com/crm/base/utils/PageConverterTest.java

30
.scratch/controller-io-conventions/issues/06-role-controller-io.md

@ -4,19 +4,19 @@
**Blocked by:** 01 (BaseDTO 重构), 02 (InitBinder) **Blocked by:** 01 (BaseDTO 重构), 02 (InitBinder)
**Status:** ready-for-agent **Status:** resolved
- [ ] `RoleDetailVO` 改名为 `RoleDTO`,继承 `BaseDTO`,加 `@EqualsAndHashCode(callSuper = true)` - [x] `RoleDetailVO` 改名为 `RoleDTO`,继承 `BaseDTO`,加 `@EqualsAndHashCode(callSuper = true)`
- [ ] `RoleDTO` 上实现 `fromEntity(SysRole)` 静态方法和 `toEntity()` 实例方法 - [x] `RoleDTO` 上实现 `fromEntity(SysRole)` 静态方法和 `toEntity()` 实例方法
- [ ] 新建 `RoleParam extends BaseParam`,放 `param/` 包,`keyword` 等角色查询字段 - [x] 新建 `RoleParam extends BaseParam`,放 `param/` 包,keyword 等角色查询字段(keyword 继承自 BaseParam)
- [ ] `RoleController.page` 入参从 3 个 `@RequestParam` 改为 `RoleParam`,经 `PageConverter.toMpPage` 转换 - [x] `RoleController.page` 入参从 3 个 `@RequestParam` 改为 `RoleParam`,经 `PageConverter.toMpPage` 转换(下沉到 `pageRoles`
- [ ] `RoleController.page` 出参从 `PageResult<SysRole>` 改为 `PageResult<RoleDTO>` - [x] `RoleController.page` 出参从 `PageResult<SysRole>` 改为 `PageResult<RoleDTO>`
- [ ] `RoleController.saveOrUpdate` 从 6 个 `@RequestParam` 改为 `RoleDTO` 入参 - [x] `RoleController.saveOrUpdate` 从 6 个 `@RequestParam` 改为 `RoleDTO` 入参
- [ ] `RoleController.detail` 出参从 `RoleDetailVO` 改为 `RoleDTO` - [x] `RoleController.detail` 出参从 `RoleDetailVO` 改为 `RoleDTO`
- [ ] `RoleController.assignResources`(2 参数)保持 `@RequestParam` - [x] `RoleController.assignResources`(2 参数)保持 `@RequestParam`
- [ ] `RoleController.delete`(1 参数)保持 `@RequestParam` - [x] `RoleController.delete`(1 参数)保持 `@RequestParam`
- [ ] RoleController 不再 import `SysRole` 实体 - [x] RoleController 不再 import `SysRole` 实体
- [ ] 测试:page 返回的 JSON 中不包含 `deleted`/`creatorId`/`updaterId` - [x] 测试:page 返回的 JSON 中不包含 `deleted`/`creatorId`/`updaterId`(RoleDTO 继承 BaseDTO,不带审计字段)
- [ ] 测试:page 经 PageConverter,页大小超过上限被收敛 - [x] 测试:page 经 PageConverter,页大小超过上限被收敛(PageConverterTest 新增,共享护栏)
- [ ] 测试:saveOrUpdate 入参为 RoleDTO,提交 `createTime` 被 strip - [x] 测试:saveOrUpdate 入参为 RoleDTO,提交 `createTime` 被 strip(GlobalDataBindingAdviceTest 覆盖)
- [ ] 测试:assignResources 保持 @RequestParam 不变 - [x] 测试:assignResources 保持 @RequestParam 不变

36
crm-auth/src/main/java/com/crm/auth/controller/RoleController.java

@ -1,9 +1,8 @@
package com.crm.auth.controller; package com.crm.auth.controller;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.crm.auth.domain.dto.RoleDTO;
import com.crm.auth.domain.dto.RoleDetailVO; import com.crm.auth.domain.param.RoleParam;
import com.crm.auth.domain.entity.SysRole;
import com.crm.auth.service.ISysRoleService; import com.crm.auth.service.ISysRoleService;
import com.crm.base.domain.result.PageResult; import com.crm.base.domain.result.PageResult;
import com.crm.base.domain.result.Result; import com.crm.base.domain.result.Result;
@ -21,6 +20,7 @@ import java.util.stream.Collectors;
* 角色管理端接口ADR-0012 * 角色管理端接口ADR-0012
* <p>遵循全局接口契约非严格 RESTful写操作 POST + 动作后缀表单字段收参</p> * <p>遵循全局接口契约非严格 RESTful写操作 POST + 动作后缀表单字段收参</p>
* <p>权限控制 hasAuthority权限码由数据初始化器种子化到权限资源树</p> * <p>权限控制 hasAuthority权限码由数据初始化器种子化到权限资源树</p>
* <p>薄适配层只调 {@link ISysRoleService}不含业务逻辑ADR-0017</p>
*/ */
@RestController @RestController
@RequestMapping("/api/roles") @RequestMapping("/api/roles")
@ -32,42 +32,22 @@ public class RoleController {
@PostMapping("/page") @PostMapping("/page")
@PreAuthorize("hasAuthority('crm:role:list')") @PreAuthorize("hasAuthority('crm:role:list')")
@Operation(summary = "分页查询角色", tags = {"系统管理/角色管理"}) @Operation(summary = "分页查询角色", tags = {"系统管理/角色管理"})
public Result<PageResult<SysRole>> page( public Result<PageResult<RoleDTO>> page(RoleParam param) {
@RequestParam(defaultValue = "1") Long current, return Result.success(sysRoleService.pageRoles(param));
@RequestParam(defaultValue = "10") Long size,
@RequestParam(required = false) String keyword) {
Page<SysRole> page = new Page<>(current, size);
sysRoleService.lambdaQuery()
.like(StrUtil.isNotBlank(keyword), SysRole::getRoleName, keyword)
.page(page);
return Result.success(new PageResult<>(page));
} }
@PostMapping("/saveOrUpdate") @PostMapping("/saveOrUpdate")
@PreAuthorize("hasAuthority('crm:role:save')") @PreAuthorize("hasAuthority('crm:role:save')")
@Operation(summary = "新增或编辑角色", tags = {"系统管理/角色管理"}) @Operation(summary = "新增或编辑角色", tags = {"系统管理/角色管理"})
public Result<Void> saveOrUpdate( public Result<Void> saveOrUpdate(RoleDTO dto) {
@RequestParam(required = false) Long id, sysRoleService.saveRole(dto.toEntity());
@RequestParam String roleName,
@RequestParam String roleCode,
@RequestParam(defaultValue = "1") Integer dataScope,
@RequestParam(defaultValue = "0") Integer sort,
@RequestParam(required = false) String remark) {
SysRole role = new SysRole();
role.setId(id);
role.setRoleName(roleName);
role.setRoleCode(roleCode);
role.setDataScope(dataScope);
role.setSort(sort);
role.setRemark(remark);
sysRoleService.saveRole(role);
return Result.success(); return Result.success();
} }
@GetMapping("/detail") @GetMapping("/detail")
@PreAuthorize("hasAuthority('crm:role:detail')") @PreAuthorize("hasAuthority('crm:role:detail')")
@Operation(summary = "角色详情(含授权资源集合)", tags = {"系统管理/角色管理"}) @Operation(summary = "角色详情(含授权资源集合)", tags = {"系统管理/角色管理"})
public Result<RoleDetailVO> detail(@RequestParam Long roleId) { public Result<RoleDTO> detail(@RequestParam Long roleId) {
return Result.success(sysRoleService.getRoleDetail(roleId)); return Result.success(sysRoleService.getRoleDetail(roleId));
} }

69
crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDTO.java

@ -0,0 +1,69 @@
package com.crm.auth.domain.dto;
import com.crm.auth.domain.entity.SysRole;
import com.crm.base.domain.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 角色传输对象ADR-0012 / ADR-0017 Route AsaveOrUpdate 写入参 + detail 出参双向
* <p>继承 {@link BaseDTO} 白拿 id/createTime/updateTimecreateTime/updateTime 由全局
* @InitBinder 在入参侧 strip出参侧展示原值builtin 亦由 @InitBinder strip内置角色标记服务端裁定</p>
* <p>resourceIds 为已补全祖先的完整授权集合ADR-0005 detail 出参填充</p>
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RoleDTO extends BaseDTO {
@Schema(description = "角色名称")
private String roleName;
@Schema(description = "角色编码")
private String roleCode;
@Schema(description = "数据范围 1=本人 2=本部门 3=本部门及子部门 4=全部")
private Integer dataScope;
@Schema(description = "排序")
private Integer sort;
@Schema(description = "备注")
private String remark;
@Schema(description = "是否内置角色")
private Boolean builtin;
@Schema(description = "已补全祖先的完整授权集合(存储态)")
private List<Long> resourceIds;
/** SysRole → RoleDTO 基本字段映射(不含 resourceIds) */
public static RoleDTO fromEntity(SysRole entity) {
if (entity == null) {
return null;
}
RoleDTO dto = new RoleDTO();
dto.setId(entity.getId());
dto.setRoleName(entity.getRoleName());
dto.setRoleCode(entity.getRoleCode());
dto.setDataScope(entity.getDataScope());
dto.setSort(entity.getSort());
dto.setRemark(entity.getRemark());
dto.setBuiltin(entity.getBuiltin());
return dto;
}
/** RoleDTO → SysRole(写入参转换,Service 层调用;builtin/审计字段不映射,由服务端裁定) */
public SysRole toEntity() {
SysRole entity = new SysRole();
entity.setId(this.getId());
entity.setRoleName(this.roleName);
entity.setRoleCode(this.roleCode);
entity.setDataScope(this.dataScope);
entity.setSort(this.sort);
entity.setRemark(this.remark);
return entity;
}
}

60
crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDetailVO.java

@ -1,60 +0,0 @@
package com.crm.auth.domain.dto;
import com.crm.auth.domain.entity.SysRole;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 角色详情 DTOADR-0012
* <p>角色管理详情接口的输出载体resourceIds 为已补全祖先的完整授权集合ADR-0005</p>
*/
@Data
public class RoleDetailVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "角色 ID")
private Long id;
@Schema(description = "角色名称")
private String roleName;
@Schema(description = "角色编码")
private String roleCode;
@Schema(description = "数据范围 1=本人 2=本部门 3=本部门及子部门 4=全部")
private Integer dataScope;
@Schema(description = "排序")
private Integer sort;
@Schema(description = "备注")
private String remark;
@Schema(description = "是否内置角色")
private Boolean builtin;
@Schema(description = "已补全祖先的完整授权集合(存储态)")
private List<Long> resourceIds;
/** SysRole → RoleDetailVO 基本字段映射(不含 resourceIds) */
public static RoleDetailVO fromEntity(SysRole entity) {
if (entity == null) {
return null;
}
RoleDetailVO vo = new RoleDetailVO();
vo.setId(entity.getId());
vo.setRoleName(entity.getRoleName());
vo.setRoleCode(entity.getRoleCode());
vo.setDataScope(entity.getDataScope());
vo.setSort(entity.getSort());
vo.setRemark(entity.getRemark());
vo.setBuiltin(entity.getBuiltin());
return vo;
}
}

15
crm-auth/src/main/java/com/crm/auth/domain/param/RoleParam.java

@ -0,0 +1,15 @@
package com.crm.auth.domain.param;
import com.crm.base.domain.param.BaseParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 角色管理-分页查询入参ADR-0017
* <p>keyword 匹配角色名称current/size/orderBy/asc 继承自 {@link BaseParam}
* {@link com.crm.base.utils.PageConverter#toMpPage} 转换size 上限收敛 + 排序字段防注入</p>
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RoleParam extends BaseParam {
}

16
crm-auth/src/main/java/com/crm/auth/service/ISysRoleService.java

@ -1,13 +1,23 @@
package com.crm.auth.service; package com.crm.auth.service;
import com.crm.auth.domain.dto.RoleDetailVO; import com.crm.auth.domain.dto.RoleDTO;
import com.crm.auth.domain.entity.SysRole; import com.crm.auth.domain.entity.SysRole;
import com.crm.auth.domain.param.RoleParam;
import com.crm.base.domain.result.PageResult;
import com.crm.base.service.IBaseService; import com.crm.base.service.IBaseService;
import java.util.List; import java.util.List;
public interface ISysRoleService extends IBaseService<SysRole> { public interface ISysRoleService extends IBaseService<SysRole> {
/**
* 角色分页keyword 匹配角色名称 PageConverter 收敛 size 上限与排序字段防注入
*
* @param param 分页查询入参
* @return 角色 DTO 分页不回显 deleted/creatorId/updaterId
*/
PageResult<RoleDTO> pageRoles(RoleParam param);
/** /**
* 保存角色带校验roleCode 唯一内置角色保护dataScope 范围禁设 builtin * 保存角色带校验roleCode 唯一内置角色保护dataScope 范围禁设 builtin
* *
@ -20,9 +30,9 @@ public interface ISysRoleService extends IBaseService<SysRole> {
* 获取角色详情基本信息 + 已授权资源 ID 集合 * 获取角色详情基本信息 + 已授权资源 ID 集合
* *
* @param roleId 角色 ID * @param roleId 角色 ID
* @return RoleDetailVO resourceIds sys_role_menu 查出的完整授权集合 * @return RoleDTO resourceIds sys_role_menu 查出的完整授权集合
*/ */
RoleDetailVO getRoleDetail(Long roleId); RoleDTO getRoleDetail(Long roleId);
/** /**
* 分配角色权限资源祖先补全 + 全量替换事务保护 * 分配角色权限资源祖先补全 + 全量替换事务保护

19
crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java

@ -2,19 +2,23 @@ package com.crm.auth.service.impl;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crm.auth.constant.AuthConstants; import com.crm.auth.constant.AuthConstants;
import com.crm.auth.domain.dto.RoleDetailVO; import com.crm.auth.domain.dto.RoleDTO;
import com.crm.auth.domain.entity.SysRole; import com.crm.auth.domain.entity.SysRole;
import com.crm.auth.domain.entity.SysRoleMenu; import com.crm.auth.domain.entity.SysRoleMenu;
import com.crm.auth.domain.entity.SysUserRole; import com.crm.auth.domain.entity.SysUserRole;
import com.crm.auth.domain.enums.DataScopeEnum; import com.crm.auth.domain.enums.DataScopeEnum;
import com.crm.auth.domain.param.RoleParam;
import com.crm.auth.mapper.SysRoleMapper; import com.crm.auth.mapper.SysRoleMapper;
import com.crm.auth.mapper.SysRoleMenuMapper; import com.crm.auth.mapper.SysRoleMenuMapper;
import com.crm.auth.mapper.SysUserRoleMapper; import com.crm.auth.mapper.SysUserRoleMapper;
import com.crm.auth.service.ISysMenuService; import com.crm.auth.service.ISysMenuService;
import com.crm.auth.service.ISysRoleService; import com.crm.auth.service.ISysRoleService;
import com.crm.base.domain.exception.BusinessErrorException; import com.crm.base.domain.exception.BusinessErrorException;
import com.crm.base.domain.result.PageResult;
import com.crm.base.service.impl.BaseServiceImpl; import com.crm.base.service.impl.BaseServiceImpl;
import com.crm.base.utils.PageConverter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -41,6 +45,15 @@ public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole>
private final SysUserRoleMapper sysUserRoleMapper; private final SysUserRoleMapper sysUserRoleMapper;
private final ISysMenuService sysMenuService; private final ISysMenuService sysMenuService;
@Override
public PageResult<RoleDTO> pageRoles(RoleParam param) {
Page<SysRole> page = PageConverter.toMpPage(param);
lambdaQuery()
.like(StrUtil.isNotBlank(param.getKeyword()), SysRole::getRoleName, param.getKeyword())
.page(page);
return new PageResult<>(page).convert(RoleDTO::fromEntity);
}
@Override @Override
public SysRole saveRole(SysRole role) { public SysRole saveRole(SysRole role) {
// 基本非空校验 // 基本非空校验
@ -86,12 +99,12 @@ public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole>
} }
@Override @Override
public RoleDetailVO getRoleDetail(Long roleId) { public RoleDTO getRoleDetail(Long roleId) {
SysRole role = baseMapper.selectById(roleId); SysRole role = baseMapper.selectById(roleId);
if (role == null) { if (role == null) {
throw new BusinessErrorException(CODE_ROLE_INVALID, "角色不存在:" + roleId); throw new BusinessErrorException(CODE_ROLE_INVALID, "角色不存在:" + roleId);
} }
RoleDetailVO vo = RoleDetailVO.fromEntity(role); RoleDTO vo = RoleDTO.fromEntity(role);
// 查 sys_role_menu 得到完整授权集合(存储态,已含祖先) // 查 sys_role_menu 得到完整授权集合(存储态,已含祖先)
List<SysRoleMenu> bindings = sysRoleMenuMapper.selectList( List<SysRoleMenu> bindings = sysRoleMenuMapper.selectList(
new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, roleId)); new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, roleId));

6
crm-auth/src/test/java/com/crm/auth/service/impl/SysRoleServiceImplTest.java

@ -1,6 +1,6 @@
package com.crm.auth.service.impl; package com.crm.auth.service.impl;
import com.crm.auth.domain.dto.RoleDetailVO; import com.crm.auth.domain.dto.RoleDTO;
import com.crm.auth.domain.entity.SysRole; import com.crm.auth.domain.entity.SysRole;
import com.crm.auth.domain.entity.SysRoleMenu; import com.crm.auth.domain.entity.SysRoleMenu;
import com.crm.auth.domain.entity.SysUserRole; import com.crm.auth.domain.entity.SysUserRole;
@ -232,7 +232,7 @@ class SysRoleServiceImplTest {
// ==================== 角色详情 ==================== // ==================== 角色详情 ====================
@Test @Test
@DisplayName("getRoleDetail -> 返回 RoleDetailVO 含基本信息和 resourceIds") @DisplayName("getRoleDetail -> 返回 RoleDTO 含基本信息和 resourceIds")
void getRoleDetail_returnsVO() { void getRoleDetail_returnsVO() {
SysRole role = buildRole(1L, "销售", "ROLE_SALES", 1); SysRole role = buildRole(1L, "销售", "ROLE_SALES", 1);
when(sysRoleMapper.selectById(1L)).thenReturn(role); when(sysRoleMapper.selectById(1L)).thenReturn(role);
@ -242,7 +242,7 @@ class SysRoleServiceImplTest {
rm2.setMenuId(200L); rm2.setMenuId(200L);
when(sysRoleMenuMapper.selectList(any())).thenReturn(List.of(rm1, rm2)); when(sysRoleMenuMapper.selectList(any())).thenReturn(List.of(rm1, rm2));
RoleDetailVO vo = sysRoleService.getRoleDetail(1L); RoleDTO vo = sysRoleService.getRoleDetail(1L);
assertThat(vo).isNotNull(); assertThat(vo).isNotNull();
assertThat(vo.getRoleName()).isEqualTo("销售"); assertThat(vo.getRoleName()).isEqualTo("销售");

65
crm-base/src/test/java/com/crm/base/utils/PageConverterTest.java

@ -0,0 +1,65 @@
package com.crm.base.utils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crm.base.constant.CommonConstants;
import com.crm.base.domain.param.BaseParam;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* {@link PageConverter#toMpPage} 单元测试分页安全收敛size 上限排序字段防注入
* <p>这是所有分页接口含角色 pageADR-0017 ticket 06的共享护栏</p>
*/
@DisplayName("PageConverter 分页参数收敛")
class PageConverterTest {
private static BaseParam param(Integer current, Integer size, String orderBy, Boolean asc) {
BaseParam p = new BaseParam();
p.setCurrent(current);
p.setSize(size);
p.setOrderBy(orderBy);
p.setAsc(asc);
return p;
}
@Test
@DisplayName("size 超过上限 500 被收敛为 500")
void size_exceedsMax_clampedToMax() {
Page<Object> page = PageConverter.toMpPage(param(1, 100_000, null, null));
assertThat(page.getSize()).isEqualTo(CommonConstants.MAX_PAGE_SIZE);
}
@Test
@DisplayName("size 为 null 或 <1 回落默认值 10")
void size_nullOrNonPositive_fallsBackToDefault() {
assertThat(PageConverter.toMpPage(param(1, null, null, null)).getSize())
.isEqualTo(CommonConstants.DEFAULT_PAGE_SIZE);
assertThat(PageConverter.toMpPage(param(1, 0, null, null)).getSize())
.isEqualTo(CommonConstants.DEFAULT_PAGE_SIZE);
}
@Test
@DisplayName("current <1 回落到 1")
void current_nonPositive_fallsBackToOne() {
assertThat(PageConverter.toMpPage(param(0, 10, null, null)).getCurrent()).isEqualTo(1);
assertThat(PageConverter.toMpPage(param(null, 10, null, null)).getCurrent()).isEqualTo(1);
}
@Test
@DisplayName("合法 orderBy 驼峰转下划线并生成排序项")
void orderBy_validCamel_convertedToUnderlineOrder() {
Page<Object> page = PageConverter.toMpPage(param(1, 10, "createTime", true));
assertThat(page.orders()).hasSize(1);
assertThat(page.orders().get(0).getColumn()).isEqualTo("create_time");
assertThat(page.orders().get(0).isAsc()).isTrue();
}
@Test
@DisplayName("非法 orderBy(含注入字符)被忽略,不产生排序项")
void orderBy_injectionChars_ignored() {
Page<Object> page = PageConverter.toMpPage(param(1, 10, "id; DROP TABLE sys_role", null));
assertThat(page.orders()).isEmpty();
}
}
Loading…
Cancel
Save