diff --git a/pom.xml b/pom.xml
index 990cdb148..8caadfb77 100644
--- a/pom.xml
+++ b/pom.xml
@@ -129,6 +129,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sql/dm/ruoyi-vue-pro-dm8.sql b/sql/dm/ruoyi-vue-pro-dm8.sql
index 0cf97945a..7c5b26b81 100644
--- a/sql/dm/ruoyi-vue-pro-dm8.sql
+++ b/sql/dm/ruoyi-vue-pro-dm8.sql
@@ -4074,6 +4074,7 @@ CREATE TABLE system_user_role (
id bigint NOT NULL PRIMARY KEY IDENTITY,
user_id bigint NOT NULL,
role_id bigint NOT NULL,
+ dept_id bigint NOT NULL,
creator varchar(64) DEFAULT '' NULL,
create_time datetime DEFAULT CURRENT_TIMESTAMP NULL,
updater varchar(64) DEFAULT '' NULL,
@@ -4085,6 +4086,7 @@ CREATE TABLE system_user_role (
COMMENT ON COLUMN system_user_role.id IS '自增编号';
COMMENT ON COLUMN system_user_role.user_id IS '用户ID';
COMMENT ON COLUMN system_user_role.role_id IS '角色ID';
+COMMENT ON COLUMN system_user_role.dept_id IS '部门ID';
COMMENT ON COLUMN system_user_role.creator IS '创建者';
COMMENT ON COLUMN system_user_role.create_time IS '创建时间';
COMMENT ON COLUMN system_user_role.updater IS '更新者';
diff --git a/sql/kingbase/ruoyi-vue-pro.sql b/sql/kingbase/ruoyi-vue-pro.sql
index 37f3b9b66..6283d85a5 100644
--- a/sql/kingbase/ruoyi-vue-pro.sql
+++ b/sql/kingbase/ruoyi-vue-pro.sql
@@ -4339,6 +4339,7 @@ CREATE TABLE system_user_role
id int8 NOT NULL,
user_id int8 NOT NULL,
role_id int8 NOT NULL,
+ dept_id int8 NOT NULL,
creator varchar(64) NULL DEFAULT '',
create_time timestamp NULL DEFAULT CURRENT_TIMESTAMP,
updater varchar(64) NULL DEFAULT '',
@@ -4353,6 +4354,7 @@ ALTER TABLE system_user_role
COMMENT ON COLUMN system_user_role.id IS '自增编号';
COMMENT ON COLUMN system_user_role.user_id IS '用户ID';
COMMENT ON COLUMN system_user_role.role_id IS '角色ID';
+COMMENT ON COLUMN system_user_role.dept_id IS '部门ID';
COMMENT ON COLUMN system_user_role.creator IS '创建者';
COMMENT ON COLUMN system_user_role.create_time IS '创建时间';
COMMENT ON COLUMN system_user_role.updater IS '更新者';
diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql
index 0c810bef8..264b17894 100644
--- a/sql/mysql/ruoyi-vue-pro.sql
+++ b/sql/mysql/ruoyi-vue-pro.sql
@@ -3581,6 +3581,7 @@ CREATE TABLE `system_user_role` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增编号',
`user_id` bigint NOT NULL COMMENT '用户ID',
`role_id` bigint NOT NULL COMMENT '角色ID',
+ `dept_id` bigint NOT NULL COMMENT '部门ID',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
diff --git a/sql/oracle/ruoyi-vue-pro.sql b/sql/oracle/ruoyi-vue-pro.sql
index 9fbf8d5ad..07264f5fc 100644
--- a/sql/oracle/ruoyi-vue-pro.sql
+++ b/sql/oracle/ruoyi-vue-pro.sql
@@ -4218,6 +4218,7 @@ CREATE TABLE system_user_role
id number NOT NULL,
user_id number NOT NULL,
role_id number NOT NULL,
+ dept_id number NOT NULL,
creator varchar2(64) DEFAULT '' NULL,
create_time date DEFAULT CURRENT_TIMESTAMP NULL,
updater varchar2(64) DEFAULT '' NULL,
@@ -4232,6 +4233,7 @@ ALTER TABLE system_user_role
COMMENT ON COLUMN system_user_role.id IS '自增编号';
COMMENT ON COLUMN system_user_role.user_id IS '用户ID';
COMMENT ON COLUMN system_user_role.role_id IS '角色ID';
+COMMENT ON COLUMN system_user_role.dept_id IS '部门ID';
COMMENT ON COLUMN system_user_role.creator IS '创建者';
COMMENT ON COLUMN system_user_role.create_time IS '创建时间';
COMMENT ON COLUMN system_user_role.updater IS '更新者';
diff --git a/sql/sqlserver/ruoyi-vue-pro.sql b/sql/sqlserver/ruoyi-vue-pro.sql
index 9368057ff..b2e0527fd 100644
--- a/sql/sqlserver/ruoyi-vue-pro.sql
+++ b/sql/sqlserver/ruoyi-vue-pro.sql
@@ -10352,6 +10352,7 @@ CREATE TABLE system_user_role
id bigint NOT NULL PRIMARY KEY IDENTITY,
user_id bigint NOT NULL,
role_id bigint NOT NULL,
+ dept_id bigint NOT NULL,
creator nvarchar(64) DEFAULT '' NULL,
create_time datetime2 DEFAULT CURRENT_TIMESTAMP NULL,
updater nvarchar(64) DEFAULT '' NULL,
@@ -10382,6 +10383,13 @@ EXEC sp_addextendedproperty
'COLUMN', N'role_id'
GO
+EXEC sp_addextendedproperty
+ 'MS_Description', N'部门ID',
+ 'SCHEMA', N'dbo',
+ 'TABLE', N'system_user_role',
+ 'COLUMN', N'dept_id'
+GO
+
EXEC sp_addextendedproperty
'MS_Description', N'创建者',
'SCHEMA', N'dbo',
diff --git a/yudao-gateway/pom.xml b/yudao-gateway/pom.xml
index fd75115d9..57595d54a 100644
--- a/yudao-gateway/pom.xml
+++ b/yudao-gateway/pom.xml
@@ -69,6 +69,10 @@
com.google.guava
guava
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/GatewayServerApplication.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/GatewayServerApplication.java
index 99984699f..5da532a82 100644
--- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/GatewayServerApplication.java
+++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/GatewayServerApplication.java
@@ -1,9 +1,12 @@
package cn.iocoder.yudao.gateway;
+import cn.iocoder.yudao.gateway.filter.front.ParamterSecretProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
+@EnableConfigurationProperties(ParamterSecretProperties.class)
public class GatewayServerApplication {
public static void main(String[] args) {
diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/front/BodyDto.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/front/BodyDto.java
new file mode 100644
index 000000000..927e85571
--- /dev/null
+++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/front/BodyDto.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.gateway.filter.front;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 响应传输对象
+ *
+ * @author feng
+ */
+@Data
+public class BodyDto implements Serializable {
+ @Serial
+ private static final long serialVersionUID = -6354714160436354659L;
+
+ private Integer code;
+
+ private String msg;
+
+ private Object data;
+
+ private String requestBody;
+
+ private String responseBody;
+}
\ No newline at end of file
diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/front/FrontSecretFilter.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/front/FrontSecretFilter.java
new file mode 100644
index 000000000..0b9a02c6b
--- /dev/null
+++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/front/FrontSecretFilter.java
@@ -0,0 +1,239 @@
+package cn.iocoder.yudao.gateway.filter.front;
+
+import cn.hutool.core.codec.Base64Encoder;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.gateway.util.SecurityFrameworkUtils;
+import cn.iocoder.yudao.gateway.util.secret.AESUtils;
+import cn.iocoder.yudao.gateway.util.secret.RSAUtils;
+import com.alibaba.nacos.common.utils.StringUtils;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.reactivestreams.Publisher;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
+import org.springframework.cloud.gateway.support.BodyInserterContext;
+import org.springframework.cloud.gateway.support.NotFoundException;
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferFactory;
+import org.springframework.core.io.buffer.DataBufferUtils;
+import org.springframework.core.io.buffer.DefaultDataBufferFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ReactiveHttpOutputMessage;
+import org.springframework.http.codec.CodecConfigurer;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.BodyInserter;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+@Slf4j
+@Component
+public class FrontSecretFilter implements GlobalFilter, Ordered {
+ @Resource
+ private ParamterSecretProperties secretProperties;
+
+ @Resource
+ private CodecConfigurer codecConfigurer;
+
+ private String aesKey;
+ private BodyDto bodyDto=new BodyDto();
+ @Override
+ public int getOrder() {
+ return Ordered.HIGHEST_PRECEDENCE+98; //在日志之前,以免日志无法打印
+ }
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ // 将 Request 中可以直接获取到的参数,设置到网关日志
+ ServerHttpRequest request = exchange.getRequest();
+
+ //筛选方法,只对get put post处理
+ String method = request.getMethod().name();
+ if (request.getMethod() != HttpMethod.POST && request.getMethod() != HttpMethod.PUT && request.getMethod() != HttpMethod.PATCH && request.getMethod() != HttpMethod.GET) {
+ // 如果不是post(新增)、put(全量修改)、patch(部分字段修改)GET(加密返回体) 操作,则直接放行
+ return chain.filter(exchange);
+ }
+
+ String secretHeader = request.getHeaders().getFirst(secretProperties.getHeader());
+ if(StrUtil.isEmpty(secretHeader)){ //无头部指示
+ return chain.filter(exchange);
+ }
+ if(!secretHeader.equals("true")) //头部指示不为true
+ return chain.filter(exchange);
+ if(!secretProperties.getEnable()) //系统配置为前后端不加密
+ return chain.filter(exchange);
+ for(String url : secretProperties.getIgnoreUrls()){ //白名单剔除
+ if(request.getURI().getPath().contains(url))
+ return chain.filter(exchange);
+ }
+
+ aesKey = exchange.getRequest().getHeaders().getFirst("frontSecKEY");//前端通过header 传递的aes key
+ try {
+ //使用rsa解密 key
+ aesKey = RSAUtils.decrypt(aesKey, secretProperties.getPrivateKey());
+ }catch (Exception e){
+ log.error("解密前端密钥失败:"+e.getMessage());
+ throw NotFoundException.create(true, "Unable to find instance for " + request.getURI().getHost());
+ }
+ // 继续 filter 过滤
+ if(method.equals(HttpMethod.POST.name()) || method.equals(HttpMethod.PUT.name())) {
+ return filterWithRequestBody(exchange, chain);
+ }
+ return filterWithoutRequestBody(exchange, chain); //get method 不拦截request
+ }
+
+ private Mono filterWithRequestBody(ServerWebExchange exchange, GatewayFilterChain chain) {
+ // 设置 Request Body 读取时,设置到网关日志
+ // 此处 codecConfigurer.getReaders() 的目的,是解决 spring.codec.max-in-memory-size 不生效
+ ServerRequest serverRequest = ServerRequest.create(exchange, codecConfigurer.getReaders());
+ Mono modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
+ //**解码
+ try {
+ String _body = body.substring(1, body.length() - 1);
+ String originalQuery = AESUtils.decrypt(_body, aesKey);
+
+ bodyDto.setRequestBody(originalQuery);
+ return Mono.just(originalQuery);
+ }catch (Exception e){
+
+ }
+ return Mono.just(body);
+ });
+
+
+ // 创建 BodyInserter 对象
+ BodyInserter, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
+ // 创建 CachedBodyOutputMessage 对象
+ HttpHeaders headers = new HttpHeaders();
+ headers.putAll(exchange.getRequest().getHeaders());
+ // the new content type will be computed by bodyInserter
+ // and then set in the request decorator
+ headers.remove(HttpHeaders.CONTENT_LENGTH); // 移除
+ CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
+ // 通过 BodyInserter 将 Request Body 写入到 CachedBodyOutputMessage 中
+ return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
+ // 包装 Request,用于缓存 Request Body
+ ServerHttpRequest decoratedRequest = requestDecorate(exchange, headers, outputMessage);
+ // 包装 Response,用于记录 Response Body
+ ServerHttpResponseDecorator decoratedResponse = encodeResponse(exchange, bodyDto);
+ return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build());
+ }));
+ }
+
+ /**
+ * 加密响应包
+ * 通过 DataBufferFactory 解决响应体分段传输问题。
+ * @param exchange
+ * @param bodyDto
+ * @return
+ */
+ private ServerHttpResponseDecorator encodeResponse(ServerWebExchange exchange, BodyDto bodyDto) {
+ ServerHttpResponse response = exchange.getResponse();
+ return new ServerHttpResponseDecorator(response) {
+
+ @Override
+ public Mono writeWith(Publisher extends DataBuffer> body) {
+ if (body instanceof Flux) {
+ DataBufferFactory bufferFactory = response.bufferFactory();
+ // 获取响应类型,如果是 json 就打印
+ String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
+ if (StringUtils.isNotBlank(originalResponseContentType)
+ && originalResponseContentType.contains("application/json")) {
+ Flux extends DataBuffer> fluxBody = Flux.from(body);
+ return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
+ // 设置 response body 到网关日志
+ byte[] content = readContent(dataBuffers);
+ String responseResult = new String(content, StandardCharsets.UTF_8);
+ //加密
+ bodyDto.setResponseBody(responseResult);
+ log.info("aesKey:"+aesKey);
+ String _content = AESUtils.encrypt(responseResult, (aesKey));
+ log.info("响应整体加密:"+_content);
+ // 响应
+ return bufferFactory.wrap(_content.getBytes());
+ }));
+ }
+ }
+ // if body is not a flux. never got there.
+ return super.writeWith(body);
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ HttpHeaders httpHeaders = getDelegate().getHeaders();
+// if(!httpHeaders.containsKey(secretProperties.getHeader())){ //此处不写也是可以的,在前端可以通过response.requst得到
+// httpHeaders.add(secretProperties.getHeader(), "true");
+// }
+ return httpHeaders;
+ }
+ };
+ }
+
+ private Mono filterWithoutRequestBody(ServerWebExchange exchange, GatewayFilterChain chain) {
+ // 包装 Response,用于记录 Response Body
+ ServerHttpResponseDecorator decoratedResponse = encodeResponse(exchange, bodyDto);
+ return chain.filter(exchange.mutate().response(decoratedResponse).build());
+ }
+
+ // ========== 参考 ModifyResponseBodyGatewayFilterFactory 中的方法 ==========
+
+ private byte[] readContent(List extends DataBuffer> dataBuffers) {
+ // 合并多个流集合,解决返回体分段传输
+ DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
+ DataBuffer join = dataBufferFactory.join(dataBuffers);
+ byte[] content = new byte[join.readableByteCount()];
+ join.read(content);
+ // 释放掉内存
+ DataBufferUtils.release(join);
+ return content;
+ }
+
+ /**
+ * 请求装饰器,支持重新计算 headers、body 缓存
+ *
+ * @param exchange 请求
+ * @param headers 请求头
+ * @param outputMessage body 缓存
+ * @return 请求装饰器
+ */
+ private ServerHttpRequestDecorator requestDecorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
+ return new ServerHttpRequestDecorator(exchange.getRequest()) {
+
+ @Override
+ public HttpHeaders getHeaders() {
+ long contentLength = headers.getContentLength();
+ HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.putAll(super.getHeaders());
+ if (contentLength > 0) {
+ httpHeaders.setContentLength(contentLength);
+ } else {
+ // TODO: this causes a 'HTTP/1.1 411 Length Required' // on
+ // httpbin.org
+ httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
+ }
+ return httpHeaders;
+ }
+
+ @Override
+ public Flux getBody() {
+ return outputMessage.getBody();
+ }
+ };
+ }
+}
diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/front/ParamterSecretProperties.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/front/ParamterSecretProperties.java
new file mode 100755
index 000000000..ff3f02322
--- /dev/null
+++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/front/ParamterSecretProperties.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.gateway.filter.front;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * 参数加密配置属性类 gareway globalfilter没起作用
+ * 请求需要带上 ENCPARAMTER=true 的请求头,才会进行参数加密
+ * @author atuchina
+ */
+@ConfigurationProperties(prefix = "yudao.front.secret")
+@Data
+public class ParamterSecretProperties {
+ /**
+ * 参数加是否开启
+ */
+ private static final Boolean ENABLE_DEFAULT = true;
+
+ /**
+ * 是否开启
+ */
+ private Boolean enable = ENABLE_DEFAULT;
+
+ /**
+ * 需要忽略参数加密的请求
+ *
+ * 这里可配置部分请求是不需要加密的
+ */
+ private Set ignoreUrls = Collections.emptySet();
+
+ private String header = "ENCPARAMTER"; // 请求头名称
+
+ @NotNull(message = "前后端参数加密 密钥 不能为空")
+ private String privateKey; // 加密秘钥
+}
diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java
index b01343f8d..e4eea5aa4 100644
--- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java
+++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java
@@ -98,7 +98,7 @@ public class AccessLogFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
- return Ordered.HIGHEST_PRECEDENCE;
+ return Ordered.HIGHEST_PRECEDENCE+99;
}
@Override
diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/AESUtils.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/AESUtils.java
new file mode 100644
index 000000000..00c83f97d
--- /dev/null
+++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/AESUtils.java
@@ -0,0 +1,216 @@
+package cn.iocoder.yudao.gateway.util.secret;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+
+
+/**
+ * 功能:AES 工具类
+ * 说明:对称分组密码算法
+ * @author fir
+ * @date 2020-5-20 11:25
+ */
+@Slf4j
+@SuppressWarnings("all")
+public class AESUtils {
+ private static final Logger logger = LoggerFactory.getLogger(AESUtils.class);
+
+ public final static String KEY_ALGORITHMS = "AES";
+ public final static int KEY_SIZE = 128;
+
+ /**
+ * 生成AES密钥,base64编码格式 (128)
+ * @return
+ * @throws Exception
+ */
+ public static String getKeyAES_128() throws Exception{
+ KeyGenerator keyGen = KeyGenerator.getInstance(KEY_ALGORITHMS);
+ keyGen.init(KEY_SIZE);
+ SecretKey key = keyGen.generateKey();
+ String base64str = Base64.encodeBase64String(key.getEncoded());
+ return base64str;
+ }
+
+ /**
+ * 生成AES密钥,base64编码格式 (256)
+ * @return
+ * @throws Exception
+ */
+ public static String getKeyAES_256() throws Exception{
+ // 256需要换jar包暂时用128
+ String base64str = getKeyAES_128();
+ return base64str;
+ }
+
+ /**
+ * 根据base64Key获取SecretKey对象
+ * @param base64Key
+ * @return
+ */
+ public static SecretKey loadKeyAES(String base64Key) {
+ byte[] bytes = Base64.decodeBase64(base64Key);
+ SecretKeySpec secretKeySpec = new SecretKeySpec(bytes, KEY_ALGORITHMS);
+ return secretKeySpec;
+ }
+
+
+ /**
+ * 生成SecretKey, base64对象
+ *
+ * @return
+ */
+ public static String generateKeyAES() {
+ String keyBase64Str = null;
+ String base64Key = "";
+ try {
+ base64Key = AESUtils.getKeyAES_128();
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.error("AES密钥生成失败");
+ }
+ if(!base64Key.equals("")){
+ byte[] bytes = Base64.decodeBase64(base64Key);
+ SecretKeySpec secretKeySpec = new SecretKeySpec(bytes, KEY_ALGORITHMS);
+ keyBase64Str = Base64.encodeBase64String(secretKeySpec.getEncoded());
+ }
+ return keyBase64Str;
+ }
+
+
+ /**
+ * AES 加密字符串,SecretKey对象
+ *
+ * @param encryptData
+ * @param key
+ * @param encode
+ * @return
+ */
+ public static String encrypt(String encryptData, SecretKey key, String encode) {
+ try {
+ final Cipher cipher = Cipher.getInstance(KEY_ALGORITHMS);
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ byte[] encryptBytes = encryptData.getBytes(encode);
+ byte[] result = cipher.doFinal(encryptBytes);
+ return Base64.encodeBase64String(result);
+ } catch (Exception e) {
+ logger.error("加密异常:" + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * AES 加密字符串,base64Key对象
+ *
+ * @param encryptData
+ * @param base64Key
+ * @param encode
+ * @return
+ */
+ public static String encrypt(String encryptData, String base64Key, String encode) {
+ SecretKey key = loadKeyAES(base64Key);
+ try {
+ final Cipher cipher = Cipher.getInstance(KEY_ALGORITHMS);
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ byte[] encryptBytes = encryptData.getBytes(encode);
+ byte[] result = cipher.doFinal(encryptBytes);
+ return Base64.encodeBase64String(result);
+ } catch (Exception e) {
+ logger.error("加密异常:" + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * AES 加密字符串,base64Key对象
+ *
+ * @param encryptData
+ * @param base64Key
+ * @return
+ */
+ public static String encrypt(String encryptData, String base64Key) {
+ SecretKey key = loadKeyAES(base64Key);
+ try {
+ final Cipher cipher = Cipher.getInstance(KEY_ALGORITHMS);
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ byte[] encryptBytes = encryptData.getBytes(String.valueOf(StandardCharsets.UTF_8));
+ byte[] result = cipher.doFinal(encryptBytes);
+ return Base64.encodeBase64String(result);
+ } catch (Exception e) {
+ logger.error("加密异常:" + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * AES 解密字符串,SecretKey对象
+ *
+ * @param decryptData
+ * @param key
+ * @param encode
+ * @return
+ */
+ public static String decrypt(String decryptData, SecretKey key, String encode) {
+ try {
+ final Cipher cipher = Cipher.getInstance(KEY_ALGORITHMS);
+ cipher.init(Cipher.DECRYPT_MODE, key);
+ byte[] decryptBytes = Base64.decodeBase64(decryptData);
+ byte[] result = cipher.doFinal(decryptBytes);
+ return new String(result, encode);
+ } catch (Exception e) {
+ logger.error("加密异常:" + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * AES 解密字符串,base64Key对象
+ *
+ * @param decryptData
+ * @param base64Key
+ * @param encode
+ * @return
+ */
+ public static String decrypt(String decryptData, String base64Key, String encode) {
+ SecretKey key = loadKeyAES(base64Key);
+ try {
+ final Cipher cipher = Cipher.getInstance(KEY_ALGORITHMS);
+ cipher.init(Cipher.DECRYPT_MODE, key);
+ byte[] decryptBytes = Base64.decodeBase64(decryptData);
+ byte[] result = cipher.doFinal(decryptBytes);
+ return new String(result, encode);
+ } catch (Exception e) {
+ logger.error("加密异常:" + e.getMessage());
+ return null;
+ }
+ }
+
+
+ /**
+ * AES 解密字符串,base64Key对象
+ *
+ * @param decryptData
+ * @param base64Key
+ * @return
+ */
+ public static String decrypt(String decryptData, String base64Key) {
+ SecretKey key = loadKeyAES(base64Key);
+ try {
+ final Cipher cipher = Cipher.getInstance(KEY_ALGORITHMS);
+ cipher.init(Cipher.DECRYPT_MODE, key);
+ byte[] decryptBytes = Base64.decodeBase64(decryptData);
+ byte[] result = cipher.doFinal(decryptBytes);
+ return new String(result, String.valueOf(StandardCharsets.UTF_8));
+ } catch (Exception e) {
+ logger.error("解密异常:" + e.getMessage());
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/MD5Utils.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/MD5Utils.java
new file mode 100644
index 000000000..e1a808a6d
--- /dev/null
+++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/MD5Utils.java
@@ -0,0 +1,88 @@
+package cn.iocoder.yudao.gateway.util.secret;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+
+/**
+ * @author fir
+ */
+public class MD5Utils {
+ private static MessageDigest md;
+ static {
+ try {
+ //初始化摘要对象
+ md = MessageDigest.getInstance("md5");
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * 获得字符串的md5值
+ *
+ * @param str 字符串
+ * @return MD5值
+ */
+ public static String generateMd5ForString(String str){
+ //更新摘要数据
+ md.update(str.getBytes());
+ //生成摘要数组
+ byte[] digest = md.digest();
+ //清空摘要数据,以便下次使用
+ md.reset();
+ return formatByteArrayToString(digest);
+ }
+
+
+ /**
+ * 获得文件的md5值
+ *
+ * @param file 文件对象
+ * @return 文件MD5值
+ * @throws IOException 文件流读取异常
+ */
+ public static String generateMd5ForFile(File file) throws IOException {
+ //创建文件输入流
+ FileInputStream fis = new FileInputStream(file);
+ //将文件中的数据写入md对象
+ byte[] buffer = new byte[1024];
+ int len;
+ while ((len = fis.read(buffer)) != -1) {
+ md.update(buffer, 0, len);
+ }
+ fis.close();
+ //生成摘要数组
+ byte[] digest = md.digest();
+ //清空摘要数据,以便下次使用
+ md.reset();
+ return formatByteArrayToString(digest);
+ }
+
+
+ /**
+ * 将摘要字节数组转换为md5值
+ *
+ * @param digest 字节数组
+ * @return MD5数值
+ */
+ public static String formatByteArrayToString(byte[] digest) {
+ //创建sb用于保存md5值
+ StringBuilder sb = new StringBuilder();
+ int temp;
+ for (byte b : digest) {
+ //将数据转化为0到255之间的数据
+ temp = b & 0xff;
+ if (temp < 16) {
+ sb.append(0);
+ }
+ //Integer.toHexString(temp)将10进制数字转换为16进制
+ sb.append(Integer.toHexString(temp));
+ }
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/RSAUtils.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/RSAUtils.java
new file mode 100644
index 000000000..fa7312f93
--- /dev/null
+++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/RSAUtils.java
@@ -0,0 +1,327 @@
+package cn.iocoder.yudao.gateway.util.secret;
+
+import cn.hutool.core.io.file.FileReader;
+import cn.hutool.core.io.file.FileWriter;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.asymmetric.KeyType;
+import cn.hutool.crypto.asymmetric.RSA;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+
+import java.nio.charset.StandardCharsets;
+import java.security.*;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * 非对称加密工具方法
+ *
+ * @author fir
+ */
+@Slf4j
+public class RSAUtils {
+
+
+ private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
+ /**
+ * 类型
+ */
+ public static final String ENCRYPT_TYPE = "RSA";
+
+
+ /**
+ * 公钥名称
+ */
+ public static final String PUBLIC_KEY = "publicKey";
+
+
+ /**
+ * 私钥名称
+ */
+ public static final String PRIVATE_KEY = "privateKey";
+
+
+ /**
+ * 生成公钥私钥对
+ *
+ * @return 公钥私钥对
+ */
+ public static Map generateKey() {
+ Map map = new HashMap<>();
+ KeyPair pair = SecureUtil.generateKeyPair(ENCRYPT_TYPE);
+ PrivateKey privateKey = pair.getPrivate();
+ PublicKey publicKey = pair.getPublic();
+
+
+ String publicKeyStr = Base64.encodeBase64String(publicKey.getEncoded());
+ String privateKeyStr = Base64.encodeBase64String(privateKey.getEncoded());
+ map.put(PUBLIC_KEY, publicKeyStr);
+ map.put(PRIVATE_KEY, privateKeyStr);
+
+ return map;
+ }
+
+
+ /**
+ * 从文件中读取公钥
+ *
+ * @param filename 公钥保存路径
+ * @return 公钥字符串
+ */
+ public static String getPublicKey(String filename) {
+ //默认UTF-8编码,可以在构造中传入第二个参数做为编码
+ FileReader fileReader = new FileReader(filename);
+ return fileReader.readString();
+ }
+
+
+ /**
+ * 从文件中读取密钥
+ *
+ * @param filename 私钥保存路径
+ * @return 私钥字符串
+ */
+ public static String getPrivateKey(String filename) {
+ //默认UTF-8编码,可以在构造中传入第二个参数做为编码
+ FileReader fileReader = new FileReader(filename);
+ return fileReader.readString();
+ }
+
+
+ /**
+ * 公钥加密
+ *
+ * @param content 要加密的内容
+ * @param publicKey 公钥
+ */
+ public static String encrypt(String content, PublicKey publicKey) {
+ try {
+ RSA rsa = new RSA(null, publicKey);
+ return rsa.encryptBase64(content, KeyType.PublicKey);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 公钥加密
+ *
+ * @param content 要加密的内容
+ * @param publicKey 公钥(base64字符串)
+ */
+ public static String encrypt(String content, String publicKey) {
+ try {
+ RSA rsa = new RSA(null, publicKey);
+ return rsa.encryptBase64(content, KeyType.PublicKey);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 公钥加密-分段加密(解密时需要分段解密)
+ *
+ * @param t 要加密的内容(泛型对象,转化为Json字符串加密)
+ * @param publicKey 公钥(base64字符串)
+ */
+ public static String encryptSection(T t, String publicKey) {
+
+ String content = JSONObject.toJSONString(t);
+ try {
+ return encryptSection(content, publicKey);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 公钥加密-分段加密(解密时需要分段解密)
+ *
+ * @param content 要加密的内容
+ * @param publicKey 公钥(base64字符串)
+ */
+ public static String encryptSection(String content, String publicKey) {
+ try {
+ RSA rsa = new RSA(null, publicKey);
+
+ int blockSize = 117;
+ int encryptedLength = content.length();
+ StringBuilder decryptedBlocks = new StringBuilder();
+ // 拆分加密文本为块并逐个解密
+ for (int i = 0; i < encryptedLength; i += blockSize) {
+ int b = i + blockSize;
+ if (b > encryptedLength) {
+ b = encryptedLength;
+ }
+ String block = content.substring(i, b);
+ String decryptedBlock = rsa.encryptBase64(block, KeyType.PublicKey);
+ decryptedBlocks.append(decryptedBlock);
+ }
+
+ return decryptedBlocks.toString();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 私钥解密
+ *
+ * @param content 要解密的内容
+ * @param privateKey 私钥
+ */
+ public static String decrypt(String content, PrivateKey privateKey) {
+ try {
+ RSA rsa = new RSA(privateKey, null);
+ return rsa.decryptStr(content, KeyType.PrivateKey);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 私钥解密
+ *
+ * @param content 要解密的内容
+ * @param privateKey 私钥(base64字符串)
+ */
+ public static String decrypt(String content, String privateKey) {
+
+ try {
+ RSA rsa = new RSA(privateKey, null);
+ return rsa.decryptStr(content, KeyType.PrivateKey);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 私钥解密-(只能解密分段解密的数据)
+ *
+ * @param content 要解密的内容 (只能解密分段解密的数据)
+ * @param privateKey 私钥(base64字符串)
+ */
+ public static String decryptSection(String content, String privateKey) {
+ try {
+ RSA rsa = new RSA(privateKey, null);
+
+ int blockSize = 172;
+ int encryptedLength = content.length();
+ StringBuilder decryptedBlocks = new StringBuilder();
+ // 拆分加密文本为块并逐个解密
+ for (int i = 0; i < encryptedLength; i += blockSize) {
+ int b = i + blockSize;
+ if (b > encryptedLength) {
+ b = encryptedLength;
+ }
+ String block = content.substring(i, b);
+ String decryptedBlock = rsa.decryptStr(block, KeyType.PrivateKey);
+ decryptedBlocks.append(decryptedBlock);
+
+ }
+
+ return decryptedBlocks.toString();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 获取公私钥-请获取一次后保存公私钥使用
+ *
+ * @param publicKeyFilename 公钥生成的路径
+ * @param privateKeyFilename 私钥生成的路径
+ */
+ public static void generateKeyPair(String publicKeyFilename, String privateKeyFilename) {
+ try {
+ KeyPair pair = SecureUtil.generateKeyPair(ENCRYPT_TYPE);
+ PrivateKey privateKey = pair.getPrivate();
+ PublicKey publicKey = pair.getPublic();
+ // 获取 公钥和私钥 的 编码格式(通过该 编码格式 可以反过来 生成公钥和私钥对象)
+ byte[] pubEncBytes = publicKey.getEncoded();
+ byte[] priEncBytes = privateKey.getEncoded();
+
+ // 把 公钥和私钥 的 编码格式 转换为 Base64文本 方便保存
+ String pubEncBase64 = Base64.encodeBase64String(pubEncBytes);//new BASE64Encoder().encode(pubEncBytes);
+ String priEncBase64 = Base64.encodeBase64String(priEncBytes);//new BASE64Encoder().encode(priEncBytes);
+
+ FileWriter pub = new FileWriter(publicKeyFilename);
+ FileWriter pri = new FileWriter(privateKeyFilename);
+ pub.write(pubEncBase64);
+ pri.write(priEncBase64);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ // - - - - - - - - - - - - - - - - - - - - SIGN 签名,验签 - - - - - - - - - - - - - - - - - - - - //
+
+ /**
+ * 加签:生成报文签名
+ *
+ * @param content 报文内容
+ * @param privateKey 私钥
+ * @param encode 编码
+ * @return 签名
+ */
+ public static String doSign(String content, String privateKey, String encode) {
+ try {
+ String unSign = Base64.encodeBase64String(content.getBytes(StandardCharsets.UTF_8));
+ byte[] privateKeys = Base64.decodeBase64(privateKey.getBytes());
+ PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeys);
+ KeyFactory mykeyFactory = KeyFactory.getInstance(ENCRYPT_TYPE);
+ PrivateKey psbcPrivateKey = mykeyFactory.generatePrivate(privateKeySpec);
+ Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
+ signature.initSign(psbcPrivateKey);
+ signature.update(unSign.getBytes(encode));
+ byte[] signed = signature.sign();
+ return Base64.encodeBase64String(signed);
+ } catch (Exception e) {
+ log.error("生成报文签名出现异常");
+ }
+ return null;
+ }
+
+
+ /**
+ * 验证:验证签名信息
+ *
+ * @param content 签名报文
+ * @param signed 签名信息
+ * @param publicKey 公钥
+ * @param encode 编码格式
+ * @return 通过/失败
+ */
+ public static boolean doCheck(String content, String signed, PublicKey publicKey, String encode) {
+ try {
+ // 解密之前先把content明文,进行base64转码
+ String unsigned = Base64.encodeBase64String(content.getBytes(encode));
+ Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
+ signature.initVerify(publicKey);
+ signature.update(unsigned.getBytes(encode));
+ return signature.verify(Base64.decodeBase64(signed));
+ } catch (Exception e) {
+ log.error("报文验证签名出现异常");
+ }
+ return false;
+ }
+}
diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/SaltedHashUtils.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/SaltedHashUtils.java
new file mode 100644
index 000000000..6960f0ded
--- /dev/null
+++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/util/secret/SaltedHashUtils.java
@@ -0,0 +1,75 @@
+package cn.iocoder.yudao.gateway.util.secret;
+
+import org.springframework.security.crypto.bcrypt.BCrypt;
+
+import java.security.SecureRandom;
+
+
+/**
+ * 盐加密函数函数
+ *
+ * @author fir
+ * @date 2023/7/13 21:19
+ */
+public class SaltedHashUtils {
+
+
+ /**
+ * 盐的长度
+ */
+ private static final int SALT_LENGTH = 16;
+
+
+ /**
+ * 盐值生成
+ *
+ * @return 16位盐值
+ */
+ public static String generateSalt() {
+ SecureRandom secureRandom = new SecureRandom();
+ byte[] salt = new byte[SALT_LENGTH];
+ secureRandom.nextBytes(salt);
+ return bytesToHex(salt);
+ }
+
+
+ /**
+ * 密码加盐
+ *
+ * @param password 密码
+ * @param salt 盐值
+ * @return 加盐数值
+ */
+ public static String generateHash(String password, String salt) {
+ String saltedPassword = salt + password;
+ return BCrypt.hashpw(saltedPassword, BCrypt.gensalt());
+ }
+
+
+ /**
+ * 密码,盐 对比 盐后数值 是否相同
+ *
+ * @param password 密码
+ * @param salt 盐值
+ * @return 相同:true/不相同:false
+ */
+ public static boolean validatePassword(String password, String salt, String hashedPassword) {
+ String saltedPassword = salt + password;
+ return BCrypt.checkpw(saltedPassword, hashedPassword);
+ }
+
+
+ /**
+ * 字节转16进制
+ *
+ * @param bytes 字节流
+ * @return 字符串
+ */
+ private static String bytesToHex(byte[] bytes) {
+ StringBuilder result = new StringBuilder();
+ for (byte b : bytes) {
+ result.append(String.format("%02x", b));
+ }
+ return result.toString();
+ }
+}
diff --git a/yudao-gateway/src/main/resources/application.yaml b/yudao-gateway/src/main/resources/application.yaml
index f59435246..928fb72f1 100644
--- a/yudao-gateway/src/main/resources/application.yaml
+++ b/yudao-gateway/src/main/resources/application.yaml
@@ -1,4 +1,19 @@
+--- #################### 注册中心 + 配置中心相关配置 ####################
+
spring:
+ cloud:
+ nacos:
+ server-addr: ${NACOS_HOST_ADDR:nacos.default}:${NACOS_HOST_PORT:8848}
+ username: ${NACOS_USERNAME}
+ password: ${NACOS_PASSWORD}
+ discovery: # 【配置中心】配置项
+ namespace: ${NACOS_NAMESPACE:ebe55be3-9630-4f18-9f8a-71b578896355} # 命名空间。这里使用 dev 开发环境
+ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
+ metadata:
+ version: 1.0.0 # 服务实例的版本号,可用于灰度发布
+ config: # 【注册中心】配置项
+ namespace: ${NACOS_NAMESPACE:ebe55be3-9630-4f18-9f8a-71b578896355} # 命名空间。这里使用 dev 开发环境
+ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
application:
name: gateway-server
@@ -13,165 +28,7 @@ spring:
- optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置
- cloud:
- # Spring Cloud Gateway 配置项,对应 GatewayProperties 类
- gateway:
- # 路由配置项,对应 RouteDefinition 数组
- routes:
- ## system-server 服务
- - id: system-admin-api # 路由的编号
- uri: grayLb://system-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/system/**
- filters:
- - RewritePath=/admin-api/system/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
- - id: system-app-api # 路由的编号
- uri: grayLb://system-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/app-api/system/**
- filters:
- - RewritePath=/app-api/system/v3/api-docs, /v3/api-docs
- ## infra-server 服务
- - id: infra-admin-api # 路由的编号
- uri: grayLb://infra-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/infra/**
- filters:
- - RewritePath=/admin-api/infra/v3/api-docs, /v3/api-docs
- - id: infra-app-api # 路由的编号
- uri: grayLb://infra-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/app-api/infra/**
- filters:
- - RewritePath=/app-api/infra/v3/api-docs, /v3/api-docs
- - id: infra-spring-boot-admin # 路由的编号(Spring Boot Admin)
- uri: grayLb://infra-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin/**
- - id: infra-websocket # 路由的编号(WebSocket)
- uri: grayLb://infra-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/infra/ws/**
- ## member-server 服务
- - id: member-admin-api # 路由的编号
- uri: grayLb://member-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/member/**
- filters:
- - RewritePath=/admin-api/member/v3/api-docs, /v3/api-docs
- - id: member-app-api # 路由的编号
- uri: grayLb://member-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/app-api/member/**
- filters:
- - RewritePath=/app-api/member/v3/api-docs, /v3/api-docs
- ## bpm-server 服务
- - id: bpm-admin-api # 路由的编号
- uri: grayLb://bpm-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/bpm/**
- filters:
- - RewritePath=/admin-api/bpm/v3/api-docs, /v3/api-docs
- ## report-server 服务
- - id: report-admin-api # 路由的编号
- uri: grayLb://report-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/report/**
- filters:
- - RewritePath=/admin-api/report/v3/api-docs, /v3/api-docs
- - id: report-jimu # 路由的编号(积木报表)
- uri: grayLb://report-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/jmreport/**
- ## pay-server 服务
- - id: pay-admin-api # 路由的编号
- uri: grayLb://pay-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/pay/**
- filters:
- - RewritePath=/admin-api/pay/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
- - id: pay-app-api # 路由的编号
- uri: grayLb://pay-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/app-api/pay/**
- filters:
- - RewritePath=/app-api/pay/v3/api-docs, /v3/api-docs
- ## mp-server 服务
- - id: mp-admin-api # 路由的编号
- uri: grayLb://mp-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/mp/**
- filters:
- - RewritePath=/admin-api/mp/v3/api-docs, /v3/api-docs
- ## product-server 服务
- - id: product-admin-api # 路由的编号
- uri: grayLb://product-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/product/**
- filters:
- - RewritePath=/admin-api/product/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
- - id: product-app-api # 路由的编号
- uri: grayLb://product-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/app-api/product/**
- filters:
- - RewritePath=/app-api/product/v3/api-docs, /v3/api-docs
- ## promotion-server 服务
- - id: promotion-admin-api # 路由的编号
- uri: grayLb://promotion-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/promotion/**
- filters:
- - RewritePath=/admin-api/promotion/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
- - id: promotion-app-api # 路由的编号
- uri: grayLb://promotion-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/app-api/promotion/**
- filters:
- - RewritePath=/app-api/promotion/v3/api-docs, /v3/api-docs
- ## trade-server 服务
- - id: trade-admin-api # 路由的编号
- uri: grayLb://trade-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/trade/**
- filters:
- - RewritePath=/admin-api/trade/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
- - id: trade-app-api # 路由的编号
- uri: grayLb://trade-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/app-api/trade/**
- filters:
- - RewritePath=/app-api/trade/v3/api-docs, /v3/api-docs
- ## statistics-server 服务
- - id: statistics-admin-api # 路由的编号
- uri: grayLb://statistics-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/statistics/**
- filters:
- - RewritePath=/admin-api/statistics/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
- ## erp-server 服务
- - id: erp-admin-api # 路由的编号
- uri: grayLb://erp-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/erp/**
- filters:
- - RewritePath=/admin-api/erp/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
- ## crm-server 服务
- - id: crm-admin-api # 路由的编号
- uri: grayLb://crm-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/crm/**
- filters:
- - RewritePath=/admin-api/crm/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
- ## ai-server 服务
- - id: ai-admin-api # 路由的编号
- uri: grayLb://ai-server
- predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- - Path=/admin-api/ai/**
- filters:
- - RewritePath=/admin-api/ai/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
- x-forwarded:
- prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀
+
server:
port: 48080
@@ -179,54 +36,3 @@ server:
logging:
file:
name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
-
-knife4j:
- # 聚合 Swagger 文档,参考 https://doc.xiaominfo.com/docs/action/springcloud-gateway 文档
- gateway:
- enabled: true
- routes:
- - name: system-server
- service-name: system-server
- url: /admin-api/system/v3/api-docs
- - name: infra-server
- service-name: infra-server
- url: /admin-api/infra/v3/api-docs
- - name: member-server
- service-name: member-server
- url: /admin-api/member/v3/api-docs
- - name: bpm-server
- service-name: bpm-server
- url: /admin-api/bpm/v3/api-docs
- - name: pay-server
- service-name: pay-server
- url: /admin-api/pay/v3/api-docs
- - name: mp-server
- service-name: mp-server
- url: /admin-api/mp/v3/api-docs
- - name: product-server
- service-name: product-server
- url: /admin-api/product/v3/api-docs
- - name: promotion-server
- service-name: promotion-server
- url: /admin-api/promotion/v3/api-docs
- - name: trade-server
- service-name: trade-server
- url: /admin-api/trade/v3/api-docs
- - name: statistics-server
- service-name: statistics-server
- url: /admin-api/statistics/v3/api-docs
- - name: erp-server
- service-name: erp-server
- url: /admin-api/erp/v3/api-docs
- - name: crm-server
- service-name: crm-server
- url: /admin-api/crm/v3/api-docs
- - name: ai-server
- service-name: ai-server
- url: /admin-api/ai/v3/api-docs
-
---- #################### 芋道相关配置 ####################
-
-yudao:
- info:
- version: 1.0.0
\ No newline at end of file
diff --git a/yudao-gateway/src/main/resources/application-dev.yaml b/yudao-gateway/src/test/java/application-dev.yaml
similarity index 100%
rename from yudao-gateway/src/main/resources/application-dev.yaml
rename to yudao-gateway/src/test/java/application-dev.yaml
diff --git a/yudao-gateway/src/main/resources/application-local.yaml b/yudao-gateway/src/test/java/application-local.yaml
similarity index 100%
rename from yudao-gateway/src/main/resources/application-local.yaml
rename to yudao-gateway/src/test/java/application-local.yaml
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
index 2f92adc0e..cd1698227 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
@@ -24,6 +24,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
@@ -42,6 +43,12 @@ public class FileController {
public CommonResult uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
MultipartFile file = uploadReqVO.getFile();
String path = uploadReqVO.getPath();
+ String extname = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")).toLowerCase();
+ if(StrUtil.isEmpty(extname)){
+ return error(3379,"只能上传图片文件!");
+ }
+ if(!".bmp,.jpg,.jpeg,.png".contains(extname))
+ return error(3379,"只能上传图片文件!");
return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application.yaml b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application.yaml
index 51e84e4aa..6758a6855 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application.yaml
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application.yaml
@@ -1,4 +1,20 @@
+--- #################### 注册中心 + 配置中心相关配置 ####################
+
spring:
+ cloud:
+ nacos:
+ server-addr: ${NACOS_HOST_ADDR:nacos.default}:${NACOS_HOST_PORT:8848}
+ username: ${NACOS_USERNAME}
+ password: ${NACOS_PASSWORD}
+ discovery: # 【配置中心】配置项
+ namespace: ${NACOS_NAMESPACE:ebe55be3-9630-4f18-9f8a-71b578896355} # 命名空间。这里使用 dev 开发环境
+ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
+ metadata:
+ version: 1.0.0 # 服务实例的版本号,可用于灰度发布
+ config: # 【注册中心】配置项
+ namespace: ${NACOS_NAMESPACE:ebe55be3-9630-4f18-9f8a-71b578896355} # 命名空间。这里使用 dev 开发环境
+ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
+
application:
name: infra-server
@@ -64,7 +80,7 @@ mybatis-plus:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
- id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
+ id-type: AUTO # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
# id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
# id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
# id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application-dev.yaml b/yudao-module-infra/yudao-module-infra-biz/src/test/application-dev.yaml
similarity index 100%
rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/application-dev.yaml
rename to yudao-module-infra/yudao-module-infra-biz/src/test/application-dev.yaml
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application-local.yaml b/yudao-module-infra/yudao-module-infra-biz/src/test/application-local.yaml
similarity index 100%
rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/application-local.yaml
rename to yudao-module-infra/yudao-module-infra-biz/src/test/application-local.yaml
diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java
index 463f3bb21..471f79f42 100644
--- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java
+++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginReqVO.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -28,7 +29,8 @@ public class AppAuthLoginReqVO {
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
@NotEmpty(message = "密码不能为空")
- @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+ @Length(min = 8, max = 16, message = "密码长度为 8-16 位")
+ @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$", message = "用户密码由 数字、字母 特殊字符(@#$%^&+=!之一)8-16位组成")
private String password;
// ========== 绑定社交登录时,需要传递如下参数 ==========
diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserResetPasswordReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserResetPasswordReqVO.java
index 043c881fd..54e4f5ace 100644
--- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserResetPasswordReqVO.java
+++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserResetPasswordReqVO.java
@@ -21,7 +21,8 @@ public class AppMemberUserResetPasswordReqVO {
@Schema(description = "新密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
@NotEmpty(message = "新密码不能为空")
- @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+ @Length(min = 8, max = 16, message = "密码长度为 8-16 位")
+ @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$", message = "用户密码由 数字、字母 特殊字符(@#$%^&+=!之一)8-16位组成")
private String password;
@Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdatePasswordReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdatePasswordReqVO.java
index 5319d727e..a533a6579 100644
--- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdatePasswordReqVO.java
+++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdatePasswordReqVO.java
@@ -19,7 +19,8 @@ public class AppMemberUserUpdatePasswordReqVO {
@Schema(description = "新密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
@NotEmpty(message = "新密码不能为空")
- @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+ @Length(min = 8, max = 16, message = "密码长度为 8-16 位")
+ @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$", message = "用户密码由 数字、字母 特殊字符(@#$%^&+=!之一)8-16位组成")
private String password;
@Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java
index 43b1ea9a4..ca5eb68e4 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java
@@ -28,7 +28,8 @@ public class AuthLoginReqVO {
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao")
@NotEmpty(message = "密码不能为空")
- @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+ @Length(min = 8, max = 16, message = "密码长度为 8-16 位")
+ @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$", message = "用户密码由 数字、字母 特殊字符(@#$%^&+=!之一)8-16位组成")
private String password;
// ========== 图片验证码相关 ==========
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java
index fb88a077e..89cd9dc19 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java
@@ -26,7 +26,8 @@ public class AuthRegisterReqVO {
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@NotEmpty(message = "密码不能为空")
- @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+ @Length(min = 8, max = 16, message = "密码长度为 8-16 位")
+ @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$", message = "用户密码由 数字、字母 特殊字符(@#$%^&+=!之一)8-16位组成")
private String password;
// ========== 图片验证码相关 ==========
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java
index 175afa34b..f496e3526 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java
@@ -57,7 +57,8 @@ public class TenantSaveReqVO {
private String username;
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
- @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+ @Length(min = 8, max = 16, message = "密码长度为 8-16 位")
+ @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$", message = "用户密码由 数字、字母 特殊字符(@#$%^&+=!之一)8-16位组成")
private String password;
@AssertTrue(message = "用户账号、密码不能为空")
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdatePasswordReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdatePasswordReqVO.java
index cfe37f620..3cba12f94 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdatePasswordReqVO.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdatePasswordReqVO.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.system.controller.admin.user.vo.profile;
import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Pattern;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
@@ -12,12 +13,14 @@ public class UserProfileUpdatePasswordReqVO {
@Schema(description = "旧密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@NotEmpty(message = "旧密码不能为空")
- @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+ @Length(min = 8, max = 16, message = "密码长度为 8-16 位")
+ @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$", message = "用户密码由 数字、字母 特殊字符(@#$%^&+=!之一)8-16位组成")
private String oldPassword;
@Schema(description = "新密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "654321")
@NotEmpty(message = "新密码不能为空")
- @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+ @Length(min = 8, max = 16, message = "密码长度为 8-16 位")
+ @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$", message = "用户密码由 数字、字母 特殊字符(@#$%^&+=!之一)8-16位组成")
private String newPassword;
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java
index bdcbe0a4d..651370187 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java
@@ -23,7 +23,7 @@ public class UserSaveReqVO {
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao")
@NotBlank(message = "用户账号不能为空")
- @Pattern(regexp = "^[a-zA-Z0-9]$", message = "用户账号由 数字、字母 组成")
+ @Pattern(regexp = "^[A-Za-z0-9]+$", message = "用户账号由 数字、字母 组成")
@Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符")
@DiffLogField(name = "用户账号")
private String username;
@@ -67,7 +67,9 @@ public class UserSaveReqVO {
// ========== 仅【创建】时,需要传递的字段 ==========
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
- @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+
+ @Length(min = 8, max = 16, message = "密码长度为 8-16 位")
+ @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$", message = "用户密码由 数字、字母 特殊字符(@#$%^&+=!之一)8-16位组成")
private String password;
@AssertTrue(message = "密码不能为空")
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java
index 065d0d91f..0c2db2f8c 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.system.controller.admin.user.vo.user;
import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Pattern;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
@@ -17,7 +18,9 @@ public class UserUpdatePasswordReqVO {
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@NotEmpty(message = "密码不能为空")
- @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+
+ @Length(min = 8, max = 16, message = "密码长度为 8-16 位")
+ @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,20}$", message = "用户密码由 数字、字母 特殊字符(@#$%^&+=!之一)8-16位组成")
private String password;
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/UserRoleDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/UserRoleDO.java
index 010184066..317f7aa9a 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/UserRoleDO.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/UserRoleDO.java
@@ -31,5 +31,8 @@ public class UserRoleDO extends BaseDO {
* 角色 ID
*/
private Long roleId;
-
+ /**
+ * 部门 ID
+ */
+ private Long deptId;
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java
index c0c9be8e4..0303738b3 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java
@@ -5,8 +5,9 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
-import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.*;
+import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
@@ -47,5 +48,12 @@ public interface AdminUserMapper extends BaseMapperX {
default List selectListByDeptIds(Collection deptIds) {
return selectList(AdminUserDO::getDeptId, deptIds);
}
+ @Select("SELECT update_time FROM system_user_password_time WHERE user_id = #{userId}")
+ LocalDateTime getPasswordUpdateRecord(@Param("userId") Long userId);
+ @Update("UPDATE system_user_password_time SET update_time = CURRENT_TIMESTAMP WHERE user_id = #{userId}")
+ void update_password_updatetime(@Param("userId") Long userId);
+
+ @Insert("INSERT INTO system_user_password_time(user_id,update_time) values(#{userId},CURRENT_TIMESTAMP)")
+ void insertPasswordUpdatetime(@Param("userId") Long userId);
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java
index 1cd58306b..7ef57f395 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java
@@ -107,4 +107,8 @@ public interface RedisKeyConstants {
*/
String WXA_SUBSCRIBE_TEMPLATE = "wxa_subscribe_template";
+ /**
+ * 用户登录错误次数
+ */
+ String LOGIN_ERROR_TIMES = "user_login_error_times";
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/config/DataPermissionConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/config/DataPermissionConfiguration.java
index 136866ca0..2d88b7b65 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/config/DataPermissionConfiguration.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/config/DataPermissionConfiguration.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.system.framework.datapermission.config;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRuleCustomizer;
import org.springframework.context.annotation.Bean;
@@ -20,6 +21,7 @@ public class DataPermissionConfiguration {
// dept
rule.addDeptColumn(AdminUserDO.class);
rule.addDeptColumn(DeptDO.class, "id");
+ rule.addDeptColumn(UserRoleDO.class);
// user
rule.addUserColumn(AdminUserDO.class, "id");
};
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
index 45b9ee1f5..9eb7adce7 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
@@ -1,8 +1,10 @@
package cn.iocoder.yudao.module.system.service.auth;
import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
@@ -14,6 +16,7 @@ import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants;
@@ -31,9 +34,13 @@ import jakarta.annotation.Resource;
import jakarta.validation.Validator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
@@ -64,6 +71,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
private CaptchaService captchaService;
@Resource
private SmsCodeApi smsCodeApi;
+ @Resource
+ private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它
/**
* 验证码的开关,默认为 true
@@ -76,9 +85,30 @@ public class AdminAuthServiceImpl implements AdminAuthService {
final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
// 校验账号是否存在
AdminUserDO user = userService.getUserByUsername(username);
+ LocalDateTime localDateTime = LocalDateTime.now();
+ LocalDateTime localDateTime1= userService.getPasswordUpdatetime(user.getId());
+ if (ChronoUnit.DAYS.between(localDateTime1, localDateTime) >= 90) {
+ //密码超过90天未修改
+ userService.updateUserStatus(user.getId(), 1);
+ throw exception(new ErrorCode(-333, "超过90天未修改密码,请联系管理员解锁"));
+ }
+
if (user == null) {
createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
- throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
+ //用户登录密码错误次数限制
+ String key = RedisKeyConstants.LOGIN_ERROR_TIMES + ":" + user.getTenantId() + ":" + username;
+ String times = stringRedisTemplate.opsForValue().get(key);
+ if (StrUtil.isEmpty(times)) {
+ stringRedisTemplate.opsForValue().increment(key);
+ stringRedisTemplate.expire(key, 1, TimeUnit.DAYS); //一天内
+ }
+
+ long _times = stringRedisTemplate.opsForValue().increment(key);
+ if (_times >= 6) {
+ userService.updateUserStatus(user.getId(), 1);
+ throw exception(new ErrorCode(1_002_000_099, "账号密码不正确次数过多,账号已锁定!"));
+ } else
+ throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
if (!userService.isPasswordMatch(password, user.getPassword())) {
createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java
index 2d9e12fe6..f4acfcdeb 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java
@@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
@@ -219,6 +220,8 @@ public class PermissionServiceImpl implements PermissionService {
UserRoleDO entity = new UserRoleDO();
entity.setUserId(userId);
entity.setRoleId(roleId);
+ AdminUserDO adminUserDO = userService.getUser(userId);
+ entity.setDeptId(adminUserDO.getDeptId());
return entity;
}));
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java
index 15564408d..eb21177ba 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java
@@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import jakarta.validation.Valid;
import java.io.InputStream;
+import java.time.LocalDateTime;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -215,5 +216,20 @@ public interface AdminUserService {
* @return 是否匹配
*/
boolean isPasswordMatch(String rawPassword, String encodedPassword);
+ /**
+ * 取得密码修改的时间
+ * @Author atuchina
+ * @Date 2024/6/18 10:21
+ * @param userId
+ * @return
+ */
+ LocalDateTime getPasswordUpdatetime(Long userId);
+ /**
+ * 插入密码修改时间
+ * @Author atuchina
+ * @Date 2024/6/18 10:37
+ * @param userId
+ */
+ void insertPasswordUpdatetime(Long userId);
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
index ef06d3c29..833e80d7f 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
@@ -25,6 +25,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
+import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.dept.PostService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
@@ -37,6 +38,7 @@ import jakarta.annotation.Resource;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
+import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -83,6 +85,8 @@ public class AdminUserServiceImpl implements AdminUserService {
private FileApi fileApi;
@Resource
private ConfigApi configApi;
+ @Resource
+ private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它
@Override
@Transactional(rollbackFor = Exception.class)
@@ -129,7 +133,7 @@ public class AdminUserServiceImpl implements AdminUserService {
// 2. 插入用户
AdminUserDO user = BeanUtils.toBean(registerReqVO, AdminUserDO.class);
- user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
+ user.setStatus(CommonStatusEnum.DISABLE.getStatus()); // 默认关闭
user.setPassword(encodePassword(registerReqVO.getPassword())); // 加密密码
userMapper.insert(user);
return user.getId();
@@ -196,6 +200,13 @@ public class AdminUserServiceImpl implements AdminUserService {
AdminUserDO updateObj = new AdminUserDO().setId(id);
updateObj.setPassword(encodePassword(reqVO.getNewPassword())); // 加密密码
userMapper.updateById(updateObj);
+
+ //更新密码修改时间表
+ if(userMapper.getPasswordUpdateRecord(id)!=null){
+ userMapper.update_password_updatetime(id);
+ }else{
+ userMapper.insertPasswordUpdatetime(id);
+ }
}
@Override
@@ -224,6 +235,7 @@ public class AdminUserServiceImpl implements AdminUserService {
updateObj.setPassword(encodePassword(password)); // 加密密码
userMapper.updateById(updateObj);
+ userMapper.update_password_updatetime(id);
// 3. 记录操作日志上下文
LogRecordContext.putVariable("user", user);
LogRecordContext.putVariable("newPassword", updateObj.getPassword());
@@ -232,7 +244,11 @@ public class AdminUserServiceImpl implements AdminUserService {
@Override
public void updateUserStatus(Long id, Integer status) {
// 校验用户存在
- validateUserExists(id);
+ AdminUserDO oldUser = validateUserExists(id);
+ if(oldUser.getStatus()==1) {//用户登录错误缓存次数重置
+ String key = RedisKeyConstants.LOGIN_ERROR_TIMES + ":" + oldUser.getTenantId() + ":" + oldUser.getUsername();
+ stringRedisTemplate.delete(key);
+ }
// 更新状态
AdminUserDO updateObj = new AdminUserDO();
updateObj.setId(id);
@@ -527,5 +543,17 @@ public class AdminUserServiceImpl implements AdminUserService {
private String encodePassword(String password) {
return passwordEncoder.encode(password);
}
-
+ @Override
+ public LocalDateTime getPasswordUpdatetime(Long userId){
+ LocalDateTime localDateTime = userMapper.getPasswordUpdateRecord(userId);
+ if(localDateTime==null){
+ userMapper.insertPasswordUpdatetime(userId);
+ return LocalDateTime.now();
+ }else
+ return localDateTime;
+ }
+ @Override
+ public void insertPasswordUpdatetime(Long userId){
+ userMapper.insertPasswordUpdatetime(userId);
+ }
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/application.yaml b/yudao-module-system/yudao-module-system-biz/src/main/resources/application.yaml
index 45470daf4..4ba1208a1 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/resources/application.yaml
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/application.yaml
@@ -1,4 +1,20 @@
+--- #################### 注册中心 + 配置中心相关配置 ####################
+
spring:
+ cloud:
+ nacos:
+ server-addr: ${NACOS_HOST_ADDR:nacos.default}:${NACOS_HOST_PORT:8848}
+ username: ${NACOS_USERNAME}
+ password: ${NACOS_PASSWORD}
+ discovery: # 【配置中心】配置项
+ namespace: ${NACOS_NAMESPACE:ebe55be3-9630-4f18-9f8a-71b578896355} # 命名空间。这里使用 dev 开发环境
+ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
+ metadata:
+ version: 1.0.0 # 服务实例的版本号,可用于灰度发布
+ config: # 【注册中心】配置项
+ namespace: ${NACOS_NAMESPACE:ebe55be3-9630-4f18-9f8a-71b578896355} # 命名空间。这里使用 dev 开发环境
+ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
+
application:
name: system-server
@@ -64,7 +80,7 @@ mybatis-plus:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
- id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
+ id-type: AUTO # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
# id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
# id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
# id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-dev.yaml b/yudao-module-system/yudao-module-system-biz/src/test/resources/application-dev.yaml
similarity index 100%
rename from yudao-module-system/yudao-module-system-biz/src/main/resources/application-dev.yaml
rename to yudao-module-system/yudao-module-system-biz/src/test/resources/application-dev.yaml
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml b/yudao-module-system/yudao-module-system-biz/src/test/resources/application-local.yaml
similarity index 92%
rename from yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml
rename to yudao-module-system/yudao-module-system-biz/src/test/resources/application-local.yaml
index c58e49029..fd5ec6cb9 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml
+++ b/yudao-module-system/yudao-module-system-biz/src/test/resources/application-local.yaml
@@ -3,16 +3,16 @@
spring:
cloud:
nacos:
- server-addr: 127.0.0.1:8848 # Nacos 服务器地址
- username: # Nacos 账号
- password: # Nacos 密码
+ server-addr: myserver:13721 #127.0.0.1:8848 # Nacos 服务器地址
+ username: nacos # Nacos 账号
+ password: zyx123123 # Nacos 密码
discovery: # 【配置中心】配置项
- namespace: dev # 命名空间。这里使用 dev 开发环境
+ namespace: d5c15366-e804-4c94-8288-5df56e8b7e8a # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
metadata:
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
config: # 【注册中心】配置项
- namespace: dev # 命名空间。这里使用 dev 开发环境
+ namespace: d5c15366-e804-4c94-8288-5df56e8b7e8a # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
--- #################### 数据库相关配置 ####################
@@ -56,14 +56,14 @@ spring:
primary: master
datasource:
master:
- url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
+ url: jdbc:mysql://myserver:13722/dlifevpn?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例
# url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
username: root
- password: 123456
+ password: Zyx234kk..#
# username: sa # SQL Server 连接的示例
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例
# username: SYSDBA # DM 连接的示例
@@ -72,15 +72,15 @@ spring:
lazy: true # 开启懒加载,保证启动速度
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
username: root
- password: 123456
+ password: Zyx234kk..#
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
- host: 127.0.0.1 # 地址
- port: 6379 # 端口
+ host: myserver # 地址
+ port: 13723 # 端口
database: 0 # 数据库索引
-# password: 123456 # 密码,建议生产环境开启
+ password: 123123123 # 密码,建议生产环境开启
--- #################### MQ 消息队列相关配置 ####################