操作日志参数过滤&脱敏.
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package com.orion.ops.framework.common.json.filter;
|
||||
|
||||
import com.alibaba.fastjson.serializer.ValueFilter;
|
||||
import com.orion.lang.utils.Desensitizes;
|
||||
import com.orion.lang.utils.Objects1;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字段脱敏过滤器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/12 11:34
|
||||
*/
|
||||
public class FieldDesensitizeFilter implements ValueFilter {
|
||||
|
||||
private final int keepStart;
|
||||
|
||||
private final int keepEnd;
|
||||
|
||||
private final List<String> desensitizeFields;
|
||||
|
||||
public FieldDesensitizeFilter(List<String> desensitizeFields) {
|
||||
this(1, 1, desensitizeFields);
|
||||
}
|
||||
|
||||
public FieldDesensitizeFilter(int keepStart, int keepEnd, List<String> desensitizeFields) {
|
||||
this.keepStart = keepStart;
|
||||
this.keepEnd = keepEnd;
|
||||
this.desensitizeFields = desensitizeFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(Object object, String name, Object value) {
|
||||
if (Lists.isEmpty(desensitizeFields) || !desensitizeFields.contains(name)) {
|
||||
return value;
|
||||
}
|
||||
return Desensitizes.mix(Objects1.toString(value), keepStart, keepEnd);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.orion.ops.framework.common.json.filter;
|
||||
|
||||
import com.alibaba.fastjson.serializer.PropertyFilter;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字段忽略过滤器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/12 11:21
|
||||
*/
|
||||
public class FieldIgnoreFilter implements PropertyFilter {
|
||||
|
||||
private final List<String> ignoreFields;
|
||||
|
||||
public FieldIgnoreFilter(List<String> ignoreFields) {
|
||||
this.ignoreFields = ignoreFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Object object, String name, Object value) {
|
||||
return Lists.isEmpty(ignoreFields) || !ignoreFields.contains(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 工具类
|
||||
* 配置工具类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.orion.ops.framework.common.filter;
|
||||
package com.orion.ops.framework.common.web.filter;
|
||||
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.orion.ops.framework.biz.operator.log.config;
|
||||
|
||||
import com.alibaba.fastjson.serializer.SerializeFilter;
|
||||
import com.alibaba.fastjson.serializer.ValueFilter;
|
||||
import com.orion.ops.framework.biz.operator.log.core.aspect.OperatorLogAspect;
|
||||
import com.orion.ops.framework.biz.operator.log.core.config.OperatorLogConfig;
|
||||
@@ -7,6 +8,8 @@ import com.orion.ops.framework.biz.operator.log.core.service.OperatorLogFramewor
|
||||
import com.orion.ops.framework.biz.operator.log.core.service.OperatorLogFrameworkServiceDelegate;
|
||||
import com.orion.ops.framework.biz.operator.log.core.uitls.OperatorLogs;
|
||||
import com.orion.ops.framework.common.constant.AutoConfigureOrderConst;
|
||||
import com.orion.ops.framework.common.json.filter.FieldDesensitizeFilter;
|
||||
import com.orion.ops.framework.common.json.filter.FieldIgnoreFilter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
@@ -55,9 +58,18 @@ public class OrionOperatorLogAutoConfiguration {
|
||||
@ConditionalOnBean(OperatorLogFrameworkServiceDelegate.class)
|
||||
public OperatorLogAspect operatorLogAspect(OperatorLogConfig operatorLogConfig,
|
||||
OperatorLogFrameworkService service) {
|
||||
// 设置脱敏过滤器
|
||||
OperatorLogs.setDesensitizeValueFilter(desensitizeValueFilter);
|
||||
return new OperatorLogAspect(operatorLogConfig, service);
|
||||
// 参数过滤器
|
||||
SerializeFilter[] serializeFilters = new SerializeFilter[]{
|
||||
// 忽略字段过滤器
|
||||
new FieldIgnoreFilter(operatorLogConfig.getIgnore()),
|
||||
// 脱敏字段过滤器
|
||||
new FieldDesensitizeFilter(operatorLogConfig.getDesensitize()),
|
||||
// 脱敏字段注解过滤器
|
||||
desensitizeValueFilter
|
||||
};
|
||||
// 设置过滤器到工具类中
|
||||
OperatorLogs.setSerializeFilters(serializeFilters);
|
||||
return new OperatorLogAspect(operatorLogConfig, service, serializeFilters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.orion.ops.framework.biz.operator.log.core.aspect;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.ValueFilter;
|
||||
import com.alibaba.fastjson.serializer.SerializeFilter;
|
||||
import com.orion.lang.define.thread.ExecutorBuilder;
|
||||
import com.orion.lang.define.wrapper.Ref;
|
||||
import com.orion.lang.utils.Arrays1;
|
||||
@@ -64,16 +64,17 @@ public class OperatorLogAspect {
|
||||
|
||||
private final OperatorLogFrameworkService operatorLogFrameworkService;
|
||||
|
||||
@Resource
|
||||
private ValueFilter desensitizeValueFilter;
|
||||
private final SerializeFilter[] serializeFilters;
|
||||
|
||||
@Resource
|
||||
private SecurityHolder securityHolder;
|
||||
|
||||
public OperatorLogAspect(OperatorLogConfig operatorLogConfig,
|
||||
OperatorLogFrameworkService operatorLogFrameworkService) {
|
||||
OperatorLogFrameworkService operatorLogFrameworkService,
|
||||
SerializeFilter[] serializeFilters) {
|
||||
this.operatorLogConfig = operatorLogConfig;
|
||||
this.operatorLogFrameworkService = operatorLogFrameworkService;
|
||||
this.serializeFilters = serializeFilters;
|
||||
}
|
||||
|
||||
@Around("@annotation(o)")
|
||||
@@ -274,7 +275,7 @@ public class OperatorLogAspect {
|
||||
if (ret != null) {
|
||||
if (ReturnType.JSON.equals(retType)) {
|
||||
// 脱敏
|
||||
model.setReturnValue(JSON.toJSONString(ret, desensitizeValueFilter));
|
||||
model.setReturnValue(JSON.toJSONString(ret, serializeFilters));
|
||||
} else if (ReturnType.TO_STRING.equals(retType)) {
|
||||
model.setReturnValue(JSON.toJSONString(Ref.of(Objects.toString(ret))));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.orion.ops.framework.biz.operator.log.core.config;
|
||||
|
||||
import com.orion.ops.framework.common.utils.ConfigUtils;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 操作日志配置
|
||||
*
|
||||
@@ -24,8 +27,27 @@ public class OperatorLogConfig {
|
||||
*/
|
||||
private Integer userAgentLength;
|
||||
|
||||
/**
|
||||
* 忽略记录的字段
|
||||
*/
|
||||
private List<String> ignore;
|
||||
|
||||
/**
|
||||
* 需要脱敏的字段
|
||||
*/
|
||||
private List<String> desensitize;
|
||||
|
||||
public OperatorLogConfig() {
|
||||
this.errorMessageLength = 255;
|
||||
this.userAgentLength = 128;
|
||||
}
|
||||
|
||||
public void setIgnore(List<String> ignore) {
|
||||
this.ignore = ConfigUtils.parseStringList(ignore);
|
||||
}
|
||||
|
||||
public void setDesensitize(List<String> desensitize) {
|
||||
this.desensitize = ConfigUtils.parseStringList(desensitize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.orion.ops.framework.biz.operator.log.core.uitls;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.ValueFilter;
|
||||
import com.alibaba.fastjson.serializer.SerializeFilter;
|
||||
import com.orion.ops.framework.biz.operator.log.core.constant.OperatorLogKeys;
|
||||
import com.orion.ops.framework.common.security.LoginUser;
|
||||
|
||||
@@ -19,7 +19,7 @@ public class OperatorLogs implements OperatorLogKeys {
|
||||
|
||||
private static final String UN_SAVE_FLAG = "__un__save__";
|
||||
|
||||
private static ValueFilter desensitizeValueFilter;
|
||||
private static SerializeFilter[] serializeFilters;
|
||||
|
||||
/**
|
||||
* 拓展信息
|
||||
@@ -44,6 +44,16 @@ public class OperatorLogs implements OperatorLogKeys {
|
||||
initMap().put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加参数 json
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
*/
|
||||
public static void addJson(String key, Object value) {
|
||||
initMap().put(key, JSON.parseObject(JSON.toJSONString(value, serializeFilters)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加参数
|
||||
*
|
||||
@@ -67,7 +77,7 @@ public class OperatorLogs implements OperatorLogKeys {
|
||||
add((Map<String, ?>) obj);
|
||||
return;
|
||||
}
|
||||
initMap().putAll(JSON.parseObject(JSON.toJSONString(obj, desensitizeValueFilter)));
|
||||
initMap().putAll(JSON.parseObject(JSON.toJSONString(obj, serializeFilters)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,8 +157,8 @@ public class OperatorLogs implements OperatorLogKeys {
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void setDesensitizeValueFilter(ValueFilter desensitizeValueFilter) {
|
||||
OperatorLogs.desensitizeValueFilter = desensitizeValueFilter;
|
||||
public static void setSerializeFilters(SerializeFilter[] serializeFilters) {
|
||||
OperatorLogs.serializeFilters = serializeFilters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,16 @@
|
||||
"type": "java.lang.Integer",
|
||||
"description": "userAgent 长度.",
|
||||
"defaultValue": "128"
|
||||
},
|
||||
{
|
||||
"name": "orion.operator-log.ignore",
|
||||
"type": "java.util.List",
|
||||
"description": "忽略记录的字段."
|
||||
},
|
||||
{
|
||||
"name": "orion.operator-log.desensitize",
|
||||
"type": "java.util.List",
|
||||
"description": "需要脱敏的字段."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
|
||||
/**
|
||||
@@ -29,6 +30,7 @@ public class OrionDesensitizeAutoConfiguration {
|
||||
*
|
||||
* @return fastjson 序列化脱敏过滤器
|
||||
*/
|
||||
@Primary
|
||||
@Bean
|
||||
public DesensitizeValueFilter desensitizeValueFilter() {
|
||||
return new DesensitizeValueFilter();
|
||||
|
||||
@@ -2,14 +2,12 @@ package com.orion.ops.framework.log.core.interceptor;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.serializer.PropertyFilter;
|
||||
import com.alibaba.fastjson.serializer.SerializeFilter;
|
||||
import com.alibaba.fastjson.serializer.ValueFilter;
|
||||
import com.orion.lang.utils.Arrays1;
|
||||
import com.orion.lang.utils.Desensitizes;
|
||||
import com.orion.lang.utils.Objects1;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.lang.utils.reflect.Classes;
|
||||
import com.orion.ops.framework.common.json.filter.FieldDesensitizeFilter;
|
||||
import com.orion.ops.framework.common.json.filter.FieldIgnoreFilter;
|
||||
import com.orion.ops.framework.common.meta.TraceIdHolder;
|
||||
import com.orion.ops.framework.common.security.SecurityHolder;
|
||||
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
||||
@@ -48,7 +46,7 @@ public abstract class AbstractLogPrinterInterceptor implements LogPrinterInterce
|
||||
/**
|
||||
* 字段过滤器
|
||||
*/
|
||||
protected SerializeFilter[] fieldFilters;
|
||||
protected SerializeFilter[] serializeFilters;
|
||||
|
||||
/**
|
||||
* 脱敏配置
|
||||
@@ -75,17 +73,15 @@ public abstract class AbstractLogPrinterInterceptor implements LogPrinterInterce
|
||||
public void init() {
|
||||
// 请求头过滤器
|
||||
this.headerFilter = header -> config.getHeaders().contains(header);
|
||||
// 忽略字段过滤器
|
||||
PropertyFilter ignoreFilter = (Object object, String name, Object value) -> !config.getField().getIgnore().contains(name);
|
||||
// 脱敏字段过滤器
|
||||
ValueFilter desensitizeFilter = (Object object, String name, Object value) -> {
|
||||
if (config.getField().getDesensitize().contains(name)) {
|
||||
return Desensitizes.mix(Objects1.toString(value), 1, 1);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
// 参数过滤器
|
||||
this.serializeFilters = new SerializeFilter[]{
|
||||
// 忽略字段过滤器
|
||||
new FieldIgnoreFilter(config.getField().getIgnore()),
|
||||
// 脱敏字段过滤器
|
||||
new FieldDesensitizeFilter(config.getField().getDesensitize()),
|
||||
// 脱敏字段注解过滤器
|
||||
desensitizeValueFilter
|
||||
};
|
||||
this.fieldFilters = Arrays1.of(ignoreFilter, desensitizeFilter, desensitizeValueFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,7 +175,7 @@ public abstract class AbstractLogPrinterInterceptor implements LogPrinterInterce
|
||||
return EMPTY_TAG;
|
||||
} else if (printCount == 1) {
|
||||
// 单个打印参数
|
||||
return JSON.toJSONString(args[lastPrintIndex], fieldFilters);
|
||||
return JSON.toJSONString(args[lastPrintIndex], serializeFilters);
|
||||
} else {
|
||||
// 多个打印参数
|
||||
JSONArray arr = new JSONArray();
|
||||
@@ -188,7 +184,7 @@ public abstract class AbstractLogPrinterInterceptor implements LogPrinterInterce
|
||||
arr.add(args[i]);
|
||||
}
|
||||
}
|
||||
return JSON.toJSONString(arr, fieldFilters);
|
||||
return JSON.toJSONString(arr, serializeFilters);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return ERROR_TAG;
|
||||
@@ -211,7 +207,7 @@ public abstract class AbstractLogPrinterInterceptor implements LogPrinterInterce
|
||||
return IGNORED_TAG;
|
||||
}
|
||||
try {
|
||||
return JSON.toJSONString(o, fieldFilters);
|
||||
return JSON.toJSONString(o, serializeFilters);
|
||||
} catch (Exception e) {
|
||||
return ERROR_TAG;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.orion.ops.framework.common.constant.AutoConfigureOrderConst;
|
||||
import com.orion.ops.framework.common.constant.FilterOrderConst;
|
||||
import com.orion.ops.framework.common.filter.FilterCreator;
|
||||
import com.orion.ops.framework.common.web.filter.FilterCreator;
|
||||
import com.orion.ops.framework.common.security.SecurityHolder;
|
||||
import com.orion.ops.framework.mybatis.core.cache.CacheClearFilter;
|
||||
import com.orion.ops.framework.mybatis.core.handler.FieldFillHandler;
|
||||
|
||||
@@ -2,12 +2,10 @@ package com.orion.ops.framework.web.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.ops.framework.common.constant.AutoConfigureOrderConst;
|
||||
import com.orion.ops.framework.common.constant.FilterOrderConst;
|
||||
import com.orion.ops.framework.common.filter.FilterCreator;
|
||||
import com.orion.ops.framework.common.web.filter.FilterCreator;
|
||||
import com.orion.ops.framework.web.core.filter.TraceIdFilter;
|
||||
import com.orion.ops.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import com.orion.ops.framework.web.core.handler.WrapperResultHandler;
|
||||
|
||||
@@ -158,13 +158,12 @@ orion:
|
||||
headers:
|
||||
- user-agent,accept
|
||||
- content-type
|
||||
# 下面引用了 需要注意
|
||||
field:
|
||||
ignore:
|
||||
- password,newPassword
|
||||
- metrics
|
||||
desensitize:
|
||||
- phone,phoneNumber
|
||||
- email,sendEmail
|
||||
storage:
|
||||
# 本地文件存储
|
||||
local:
|
||||
@@ -198,4 +197,8 @@ orion:
|
||||
keep-alive-seconds: 180
|
||||
operator-log:
|
||||
error-message-length: 255
|
||||
user-agent-length: 128
|
||||
user-agent-length: 128
|
||||
ignore:
|
||||
- ${orion.logging.printer.field.ignore[0]}
|
||||
- ${orion.logging.printer.field.ignore[1]}
|
||||
desensitize:
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.orion.lang.define.wrapper.DataGrid;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.office.excel.writer.exporting.ExcelExport;
|
||||
import com.orion.ops.framework.biz.operator.log.core.uitls.OperatorLogs;
|
||||
#if($cacheMeta.enableCache)
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
#end
|
||||
@@ -50,7 +49,6 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
|
||||
@Override
|
||||
public Long create${type}(${type}CreateRequest request) {
|
||||
log.info("${type}Service-create${type} request: {}", JSON.toJSONString(request));
|
||||
OperatorLogs.add(request);
|
||||
// 转换
|
||||
${type}DO record = ${type}Convert.MAPPER.to(request);
|
||||
// 查询数据是否冲突
|
||||
@@ -69,7 +67,6 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
|
||||
@Override
|
||||
public Integer update${type}ById(${type}UpdateRequest request) {
|
||||
log.info("${type}Service-update${type}ById id: {}, request: {}", request.getId(), JSON.toJSONString(request));
|
||||
OperatorLogs.add(request);
|
||||
// 查询
|
||||
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
|
||||
${type}DO record = ${typeLower}DAO.selectById(id);
|
||||
@@ -180,7 +177,6 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
|
||||
@Override
|
||||
public Integer delete${type}ById(Long id) {
|
||||
log.info("${type}Service-delete${type}ById id: {}", id);
|
||||
OperatorLogs.add(OperatorLogs.ID, id);
|
||||
// 检查数据是否存在
|
||||
${type}DO record = ${typeLower}DAO.selectById(id);
|
||||
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
|
||||
@@ -197,7 +193,6 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
|
||||
@Override
|
||||
public Integer batchDelete${type}ByIdList(List<Long> idList) {
|
||||
log.info("${type}Service-batchDelete${type}ByIdList idList: {}", idList);
|
||||
OperatorLogs.add(OperatorLogs.ID_LIST, idList);
|
||||
int effect = ${typeLower}DAO.deleteBatchIds(idList);
|
||||
log.info("${type}Service-batchDelete${type}ByIdList effect: {}", effect);
|
||||
#if($cacheMeta.enableCache)
|
||||
@@ -225,7 +220,6 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
|
||||
@Override
|
||||
public void export${type}(${type}QueryRequest request, HttpServletResponse response) throws IOException {
|
||||
log.info("${type}Service.export${type} request: {}", JSON.toJSONString(request));
|
||||
OperatorLogs.add(request);
|
||||
// 条件
|
||||
LambdaQueryWrapper<${type}DO> wrapper = this.buildQueryWrapper(request);
|
||||
// 查询
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.orion.ops.module.infra.entity.request.user;
|
||||
|
||||
import com.orion.ops.framework.desensitize.core.annotation.Desensitize;
|
||||
import com.orion.ops.framework.desensitize.core.annotation.DesensitizeObject;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -15,7 +13,6 @@ import javax.validation.constraints.NotEmpty;
|
||||
* @since 2023/7/13 22:16
|
||||
*/
|
||||
@Data
|
||||
@DesensitizeObject
|
||||
@Schema(name = "UserLoginRequest", description = "登陆请求")
|
||||
public class UserLoginRequest {
|
||||
|
||||
@@ -23,7 +20,6 @@ public class UserLoginRequest {
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Desensitize(toEmpty = true)
|
||||
@NotEmpty
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
Reference in New Issue
Block a user