Commit 9463c9ff by xlx

引入微服务接口文档

parent 72c20035
Showing with 515 additions and 1395 deletions
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-api</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -25,7 +25,7 @@
<dependency>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common-mybatis</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
<scope>compile</scope>
</dependency>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>rapidplatform-cloud</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -5,12 +5,12 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-auth</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
<!-- <relativePath/>-->
</parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-auth-idm</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
<name>matchtech-auth-idm</name>
<description>matchtech-auth-idm</description>
<packaging>jar</packaging>
......
......@@ -5,12 +5,12 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-auth</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
<!-- <relativePath/> -->
</parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-auth-local</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
<name>matchtech-auth-local</name>
<description>matchtech-auth-local</description>
<packaging>jar</packaging>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>rapidplatform-cloud</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -118,12 +118,6 @@
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
......@@ -139,10 +133,10 @@
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common-dto</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.matchtech</groupId>-->
<!-- <artifactId>matchtech-common-dto</artifactId>-->
<!-- </dependency>-->
</dependencies>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -25,7 +25,7 @@
<dependency>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common-mybatis</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
<scope>compile</scope>
</dependency>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -39,7 +39,7 @@
<dependency>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common-mybatis</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</dependency>
</dependencies>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -14,22 +14,59 @@
<description>matchtech-common-mybatis</description>
<dependencies>
<!-- MyBatis Spring Boot Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- MyBatis 测试 -->
<!-- MyBatis Spring -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.2</version>
<scope>test</scope>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<!-- pagehelper 适配 Spring Boot 3 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -24,14 +24,25 @@
</dependency>
<!-- matchtech Common Core-->
<dependency>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common-core</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.matchtech</groupId>-->
<!-- <artifactId>matchtech-common-core</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<!-- Alibaba Fastjson -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -7,7 +7,6 @@ import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.filter.Filter;
import com.matchtech.common.core.constants.Constants;
/**
* Redis使用FastJson序列化
......@@ -18,7 +17,7 @@ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR);
static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(new String[]{"com.matchtech"});
private Class<T> clazz;
......
package com.matchtech.common.redis.constant;
/**
* 通用常量信息
*
* @author matchtech
*/
public class Constants
{
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
/**
* GBK 字符集
*/
public static final String GBK = "GBK";
/**
* www主域
*/
public static final String WWW = "www.";
/**
* RMI 远程方法调用
*/
public static final String LOOKUP_RMI = "rmi:";
/**
* LDAP 远程方法调用
*/
public static final String LOOKUP_LDAP = "ldap:";
/**
* LDAPS 远程方法调用
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* http请求
*/
public static final String HTTP = "http://";
/**
* https请求
*/
public static final String HTTPS = "https://";
/**
* 成功标记
*/
public static final Integer SUCCESS = 200;
/**
* 失败标记
*/
public static final Integer FAIL = 500;
/**
* 登录成功状态
*/
public static final String LOGIN_SUCCESS_STATUS = "0";
/**
* 登录失败状态
*/
public static final String LOGIN_FAIL_STATUS = "1";
/**
* 登录成功
*/
public static final String LOGIN_SUCCESS = "Success";
/**
* 注销
*/
public static final String LOGOUT = "Logout";
/**
* 注册
*/
public static final String REGISTER = "Register";
/**
* 登录失败
*/
public static final String LOGIN_FAIL = "Error";
/**
* 所有权限标识
*/
public static final String ALL_PERMISSION = "*:*:*";
/**
* 管理员角色权限标识
*/
public static final String SUPER_ADMIN = "admin";
/**
* 当前记录起始索引
*/
public static final String PAGE_NUM = "pageNum";
/**
* 每页显示记录数
*/
public static final String PAGE_SIZE = "pageSize";
/**
* 排序列
*/
public static final String ORDER_BY_COLUMN = "orderByColumn";
/**
* 排序的方向 "desc" 或者 "asc".
*/
public static final String IS_ASC = "isAsc";
/**
* 验证码有效期(分钟)
*/
public static final long CAPTCHA_EXPIRATION = 2;
/**
* 资源映射路径 前缀
*/
public static final String RESOURCE_PREFIX = "/profile";
/**
* 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全)
*/
public static final String[] JSON_WHITELIST_STR = { "com.matchtech" };
/**
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
*/
public static final String[] JOB_WHITELIST_STR = { "com.matchtech.job.task" };
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
"org.springframework", "org.apache", "com.matchtech.common.core.utils.file" };
/**
* 树路径分隔符
*/
public static final String TREE_PATH_SEPARATOR = ",";
/**
* 超管id
*/
public static final String SUPER_ADMIN_ID = "1";
/**
* 树形结构一级节点
*/
public static final String SUPER_NODE = "0";
/**
* 是
*/
public static final String YES = "1";
/**
* 否
*/
public static final String NO = "0";
/**
* 基地固资干事角色code
*/
public static final String BASE_FA_ROLE_CODE = "BaseFa" ;
/**
* 部门负责人角色code
*/
public static final String DEPARTMENT_HEAD_ROLE_CODE = "DepartmentHead";
/**
* 固资管理员角色code
*/
public static final String FA_ADMIN_ROLE_CODE = "FaAdmin";
}
......@@ -7,7 +7,6 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.matchtech.common.core.utils.DateUtils;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RBucket;
import org.redisson.api.RKeys;
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -4,13 +4,13 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common-servlet</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
<name>matchtech-common-servlet</name>
<description>matchtech-common-servlet</description>
......@@ -25,6 +25,7 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<!-- 依赖版本由父POM管理 -->
</dependency>
<dependency>
<groupId>com.matchtech</groupId>
......@@ -32,4 +33,4 @@
</dependency>
</dependencies>
</project>
</project>
\ No newline at end of file
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>rapidplatform-cloud</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<dependencies>
<dependency>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>rapidplatform-cloud</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -15,114 +15,21 @@
</description>
<dependencies>
<!-- SpringCloud Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>4.1.2</version>
</dependency>
<!-- SpringCloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel Gateway -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!-- Sentinel Datasource Nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- SpringBoot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- SpringCloud Loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<!--验证码 -->
<dependency>
<groupId>pro.fessional</groupId>
<artifactId>kaptcha</artifactId>
</dependency>
<!-- matchtech Common Redis-->
<dependency>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common-redis</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Springdoc -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-core</artifactId>
<version>1.8.0</version>
</dependency>
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-ui</artifactId>
<version>3.0.3</version>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
</project>
\ No newline at end of file
package com.matchtech.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* 网关启动程序
*
* @author matchtech
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
@SpringBootApplication
@EnableDiscoveryClient
public class MatchtechGatewayApplication
{
public static void main(String[] args)
{
SpringApplication.run(MatchtechGatewayApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 若依网关启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \n" +
" | ( ' ) | \\ _. / ' \n" +
" |(_ o _) / _( )_ .' \n" +
" | (_,_).' __ ___(_ o _)' \n" +
" | |\\ \\ | || |(_,_)' \n" +
" | | \\ `' /| `-' / \n" +
" | | \\ / \\ / \n" +
" ''-' `'-' `-..-' ");
System.out.println("(网关启动成功");
}
}
package com.matchtech.gateway.config;
import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import static com.google.code.kaptcha.Constants.*;
/**
* 验证码配置
*
* @author matchtech
*/
@Configuration
public class CaptchaConfig
{
@Bean(name = "captchaProducer")
public DefaultKaptcha getKaptchaBean()
{
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 是否有边框 默认为true 我们可以自己设置yes,no
properties.setProperty(KAPTCHA_BORDER, "yes");
// 验证码文本字符颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
// 验证码图片宽度 默认为200
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
// 验证码图片高度 默认为50
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
// 验证码文本字符大小 默认为40
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
// KAPTCHA_SESSION_KEY
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
// 验证码文本字符长度 默认为5
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
@Bean(name = "captchaProducerMath")
public DefaultKaptcha getKaptchaBeanMath()
{
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 是否有边框 默认为true 我们可以自己设置yes,no
properties.setProperty(KAPTCHA_BORDER, "yes");
// 边框颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
// 验证码文本字符颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
// 验证码图片宽度 默认为200
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
// 验证码图片高度 默认为50
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
// 验证码文本字符大小 默认为40
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
// KAPTCHA_SESSION_KEY
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
// 验证码文本生成器
properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.gateway.config.KaptchaTextCreator");
// 验证码文本字符间距 默认为2
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
// 验证码文本字符长度 默认为5
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
// 验证码噪点颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
// 干扰实现类
properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
\ No newline at end of file
package com.matchtech.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.matchtech.gateway.handler.SentinelFallbackHandler;
/**
* 网关限流配置
*
* @author matchtech
*/
@Configuration
public class GatewayConfig
{
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelFallbackHandler sentinelGatewayExceptionHandler()
{
return new SentinelFallbackHandler();
}
}
\ No newline at end of file
package com.matchtech.gateway.config;
import java.util.Random;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
/**
* 验证码文本生成器
*
* @author matchtech
*/
public class KaptchaTextCreator extends DefaultTextCreator
{
private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
@Override
public String getText()
{
Integer result = 0;
Random random = new Random();
int x = random.nextInt(10);
int y = random.nextInt(10);
StringBuilder suChinese = new StringBuilder();
int randomoperands = random.nextInt(3);
if (randomoperands == 0)
{
result = x * y;
suChinese.append(CNUMBERS[x]);
suChinese.append("*");
suChinese.append(CNUMBERS[y]);
}
else if (randomoperands == 1)
{
if ((x != 0) && y % x == 0)
{
result = y / x;
suChinese.append(CNUMBERS[y]);
suChinese.append("/");
suChinese.append(CNUMBERS[x]);
}
else
{
result = x + y;
suChinese.append(CNUMBERS[x]);
suChinese.append("+");
suChinese.append(CNUMBERS[y]);
}
}
else if (randomoperands == 2)
{
if (x >= y)
{
result = x - y;
suChinese.append(CNUMBERS[x]);
suChinese.append("-");
suChinese.append(CNUMBERS[y]);
}
else
{
result = y - x;
suChinese.append(CNUMBERS[y]);
suChinese.append("-");
suChinese.append(CNUMBERS[x]);
}
}
else
{
result = x + y;
suChinese.append(CNUMBERS[x]);
suChinese.append("+");
suChinese.append(CNUMBERS[y]);
}
suChinese.append("=?@" + result);
return suChinese.toString();
}
}
\ No newline at end of file
package com.matchtech.gateway.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import com.matchtech.gateway.handler.ValidateCodeHandler;
/**
* 路由配置信息
*
* @author matchtech
*/
@Configuration
public class RouterFunctionConfiguration
{
@Autowired
private ValidateCodeHandler validateCodeHandler;
@SuppressWarnings("rawtypes")
@Bean
public RouterFunction routerFunction()
{
return RouterFunctions.route(
RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
validateCodeHandler);
}
}
package com.matchtech.gateway.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
* 验证码配置
*
* @author matchtech
*/
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.captcha")
public class CaptchaProperties
{
/**
* 验证码开关
*/
private Boolean enabled;
/**
* 验证码类型(math 数组计算 char 字符)
*/
private String type;
public Boolean getEnabled()
{
return enabled;
}
public void setEnabled(Boolean enabled)
{
this.enabled = enabled;
}
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
}
package com.matchtech.gateway.config.properties;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
* 放行白名单配置
*
* @author matchtech
*/
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.ignore")
public class IgnoreWhiteProperties
{
/**
* 放行白名单配置,网关不校验此处的白名单
*/
private List<String> whites = new ArrayList<>();
public List<String> getWhites()
{
return whites;
}
public void setWhites(List<String> whites)
{
this.whites = whites;
}
}
package com.matchtech.gateway.config.properties;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
* XSS跨站脚本配置
*
* @author matchtech
*/
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.xss")
public class XssProperties
{
/**
* Xss开关
*/
private Boolean enabled;
/**
* 排除路径
*/
private List<String> excludeUrls = new ArrayList<>();
public Boolean getEnabled()
{
return enabled;
}
public void setEnabled(Boolean enabled)
{
this.enabled = enabled;
}
public List<String> getExcludeUrls()
{
return excludeUrls;
}
public void setExcludeUrls(List<String> excludeUrls)
{
this.excludeUrls = excludeUrls;
}
}
package com.matchtech.gateway.filter;
import com.matchtech.common.core.constants.CacheConstants;
import com.matchtech.common.core.constants.HttpStatus;
import com.matchtech.common.core.constants.SecurityConstants;
import com.matchtech.common.core.constants.TokenConstants;
import com.matchtech.common.core.utils.JwtUtils;
import com.matchtech.common.core.utils.StringUtils;
import com.matchtech.common.redis.service.RedisService;
import com.matchtech.gateway.config.properties.IgnoreWhiteProperties;
import com.matchtech.gateway.utils.WebFluxUtil;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 网关鉴权
*
* @author matchtech
*/
@Component
public class AuthFilter implements GlobalFilter, Ordered
{
private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
// 排除过滤的 uri 地址,nacos自行添加
@Autowired
private IgnoreWhiteProperties ignoreWhite;
@Autowired
private RedisService redisService;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest.Builder mutate = request.mutate();
String url = request.getURI().getPath();
// 跳过不需要验证的路径
if (StringUtils.matches(url, ignoreWhite.getWhites()))
{
return chain.filter(exchange);
}
String token = getToken(request);
if (StringUtils.isEmpty(token))
{
return unauthorizedResponse(exchange, "令牌不能为空");
}
Claims claims = JwtUtils.parseToken(token);
if (claims == null)
{
return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
}
String userkey = JwtUtils.getUserKey(claims);
boolean islogin = redisService.hasKey(getTokenKey(userkey));
if (!islogin)
{
return unauthorizedResponse(exchange, "登录状态已过期");
}
String userid = JwtUtils.getUserId(claims);
String username = JwtUtils.getUserName(claims);
if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))
{
return unauthorizedResponse(exchange, "令牌验证失败");
}
// 设置用户信息到请求
addHeader(mutate, SecurityConstants.USER_KEY, userkey);
addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid);
addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);
// 内部请求来源参数清除
removeHeader(mutate, SecurityConstants.FROM_SOURCE);
Mono<Void> filter = chain.filter(exchange.mutate().request(mutate.build()).build());
return filter;
}
private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value)
{
if (value == null)
{
return;
}
String valueStr = value.toString();
String valueEncode = WebFluxUtil.urlEncode(valueStr);
mutate.header(name, valueEncode);
}
private void removeHeader(ServerHttpRequest.Builder mutate, String name)
{
mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
}
private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg)
{
log.error("[鉴权异常处理]请求路径:{},错误信息:{}", exchange.getRequest().getPath(), msg);
return WebFluxUtil.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED);
}
/**
* 获取缓存key
*/
private String getTokenKey(String token)
{
return CacheConstants.LOGIN_TOKEN_KEY + token;
}
/**
* 获取请求token
*/
private String getToken(ServerHttpRequest request)
{
String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_HEADER);
// 如果前端设置了令牌前缀,则裁剪掉前缀
if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
{
token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
}
return token;
}
@Override
public int getOrder()
{
return -200;
}
}
\ No newline at end of file
package com.matchtech.gateway.filter;
import com.matchtech.gateway.utils.WebFluxUtil;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* 黑名单过滤器
*
* @author matchtech
*/
@Component
public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUrlFilter.Config>
{
@Override
public GatewayFilter apply(Config config)
{
return (exchange, chain) -> {
String url = exchange.getRequest().getURI().getPath();
if (config.matchBlacklist(url))
{
return WebFluxUtil.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问");
}
return chain.filter(exchange);
};
}
public BlackListUrlFilter()
{
super(Config.class);
}
public static class Config
{
private List<String> blacklistUrl;
private List<Pattern> blacklistUrlPattern = new ArrayList<>();
public boolean matchBlacklist(String url)
{
return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find());
}
public List<String> getBlacklistUrl()
{
return blacklistUrl;
}
public void setBlacklistUrl(List<String> blacklistUrl)
{
this.blacklistUrl = blacklistUrl;
this.blacklistUrlPattern.clear();
this.blacklistUrl.forEach(url -> {
this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE));
});
}
}
}
package com.matchtech.gateway.filter;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.matchtech.common.core.utils.StringUtils;
import com.matchtech.gateway.config.properties.CaptchaProperties;
import com.matchtech.gateway.service.ValidateCodeService;
import com.matchtech.gateway.utils.WebFluxUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
/**
* 验证码过滤器
*
* @author matchtech
*/
@Component
public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object>
{
private final static String[] VALIDATE_URL = new String[] { "/auth/login", "/auth/register" };
@Autowired
private ValidateCodeService validateCodeService;
@Autowired
private CaptchaProperties captchaProperties;
private static final String CODE = "code";
private static final String UUID = "uuid";
@Override
public GatewayFilter apply(Object config)
{
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// 非登录/注册请求或验证码关闭,不处理
if (!StringUtils.equalsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled())
{
return chain.filter(exchange);
}
try
{
String rspStr = resolveBodyFromRequest(request);
JSONObject obj = JSON.parseObject(rspStr);
validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID));
}
catch (Exception e)
{
return WebFluxUtil.webFluxResponseWriter(exchange.getResponse(), e.getMessage());
}
return chain.filter(exchange);
};
}
@SuppressWarnings("deprecation")
private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest)
{
// 获取请求体
Flux<DataBuffer> body = serverHttpRequest.getBody();
AtomicReference<String> bodyRef = new AtomicReference<>();
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
bodyRef.set(charBuffer.toString());
});
return bodyRef.get();
}
}
package com.matchtech.gateway.filter;
import java.nio.charset.StandardCharsets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
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.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.matchtech.common.core.utils.StringUtils;
import com.matchtech.common.core.utils.html.EscapeUtil;
import com.matchtech.gateway.config.properties.XssProperties;
import io.netty.buffer.ByteBufAllocator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* 跨站脚本过滤器
*
* @author matchtech
*/
@Component
@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true")
public class XssFilter implements GlobalFilter, Ordered
{
// 跨站脚本的 xss 配置,nacos自行添加
@Autowired
private XssProperties xss;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
ServerHttpRequest request = exchange.getRequest();
// xss开关未开启 或 通过nacos关闭,不过滤
if (!xss.getEnabled())
{
return chain.filter(exchange);
}
// GET DELETE 不过滤
HttpMethod method = request.getMethod();
if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE)
{
return chain.filter(exchange);
}
// 非json类型,不过滤
if (!isJsonRequest(exchange))
{
return chain.filter(exchange);
}
// excludeUrls 不过滤
String url = request.getURI().getPath();
if (StringUtils.matches(url, xss.getExcludeUrls()))
{
return chain.filter(exchange);
}
ServerHttpRequestDecorator httpRequestDecorator = requestDecorator(exchange);
return chain.filter(exchange.mutate().request(httpRequestDecorator).build());
}
private ServerHttpRequestDecorator requestDecorator(ServerWebExchange exchange)
{
ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest())
{
@Override
public Flux<DataBuffer> getBody()
{
Flux<DataBuffer> body = super.getBody();
return body.buffer().map(dataBuffers -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
DataBufferUtils.release(join);
String bodyStr = new String(content, StandardCharsets.UTF_8);
// 防xss攻击过滤
bodyStr = EscapeUtil.clean(bodyStr);
// 转成字节
byte[] bytes = bodyStr.getBytes(StandardCharsets.UTF_8);
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
});
}
@Override
public HttpHeaders getHeaders()
{
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
// 由于修改了请求体的body,导致content-length长度不确定,因此需要删除原先的content-length
httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
return httpHeaders;
}
};
return serverHttpRequestDecorator;
}
/**
* 是否是Json请求
*
* @param exchange HTTP请求
*/
public boolean isJsonRequest(ServerWebExchange exchange)
{
String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
}
@Override
public int getOrder()
{
return -100;
}
}
package com.matchtech.gateway.handler;
import com.matchtech.gateway.utils.WebFluxUtil;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 网关统一异常处理
*
* @author matchtech
*/
@Order(-1)
@Configuration
public class GatewayExceptionHandler implements ErrorWebExceptionHandler
{
private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class);
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)
{
ServerHttpResponse response = exchange.getResponse();
if (exchange.getResponse().isCommitted())
{
return Mono.error(ex);
}
String msg;
if (ex instanceof NotFoundException)
{
msg = "服务未找到";
}
else if (ex instanceof ResponseStatusException)
{
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
msg = responseStatusException.getMessage();
}
else
{
msg = "内部服务器错误";
}
log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage());
return WebFluxUtil.webFluxResponseWriter(response, msg);
}
}
\ No newline at end of file
package com.matchtech.gateway.handler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.matchtech.gateway.utils.WebFluxUtil;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;
/**
* 自定义限流异常处理
*
* @author matchtech
*/
public class SentinelFallbackHandler implements WebExceptionHandler
{
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
{
return WebFluxUtil.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试");
}
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)
{
if (exchange.getResponse().isCommitted())
{
return Mono.error(ex);
}
if (!BlockException.isBlockException(ex))
{
return Mono.error(ex);
}
return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange));
}
private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable)
{
return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
}
}
package com.matchtech.gateway.handler;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import com.matchtech.common.core.exception.CaptchaException;
import com.matchtech.common.core.web.domain.AjaxResult;
import com.matchtech.gateway.service.ValidateCodeService;
import reactor.core.publisher.Mono;
/**
* 验证码获取
*
* @author matchtech
*/
@Component
public class ValidateCodeHandler implements HandlerFunction<ServerResponse>
{
@Autowired
private ValidateCodeService validateCodeService;
@Override
public Mono<ServerResponse> handle(ServerRequest serverRequest)
{
AjaxResult ajax;
try
{
ajax = validateCodeService.createCaptcha();
}
catch (CaptchaException | IOException e)
{
return Mono.error(e);
}
return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax));
}
}
package com.matchtech.gateway.service;
import java.io.IOException;
import com.matchtech.common.core.exception.CaptchaException;
import com.matchtech.common.core.web.domain.AjaxResult;
/**
* 验证码处理
*
* @author matchtech
*/
public interface ValidateCodeService
{
/**
* 生成验证码
*/
public AjaxResult createCaptcha() throws IOException, CaptchaException;
/**
* 校验验证码
*/
public void checkCaptcha(String key, String value) throws CaptchaException;
}
package com.matchtech.gateway.service.impl;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import com.matchtech.common.core.web.domain.AjaxResult;
import jakarta.annotation.Resource;
import javax.imageio.ImageIO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FastByteArrayOutputStream;
import com.google.code.kaptcha.Producer;
import com.matchtech.common.core.constants.CacheConstants;
import com.matchtech.common.core.constants.Constants;
import com.matchtech.common.core.exception.CaptchaException;
import com.matchtech.common.core.utils.StringUtils;
import com.matchtech.common.core.utils.sign.Base64;
import com.matchtech.common.core.utils.uuid.IdUtils;
import com.matchtech.common.redis.service.RedisService;
import com.matchtech.gateway.config.properties.CaptchaProperties;
import com.matchtech.gateway.service.ValidateCodeService;
/**
* 验证码实现处理
*
* @author matchtech
*/
@Service
public class ValidateCodeServiceImpl implements ValidateCodeService
{
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@Resource(name = "captchaProducerMath")
private Producer captchaProducerMath;
@Autowired
private RedisService redisService;
@Autowired
private CaptchaProperties captchaProperties;
/**
* 生成验证码
*/
@Override
public AjaxResult createCaptcha() throws IOException, CaptchaException
{
AjaxResult ajax = AjaxResult.success();
boolean captchaEnabled = captchaProperties.getEnabled();
ajax.put("captchaEnabled", captchaEnabled);
if (!captchaEnabled)
{
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
String capStr = null, code = null;
BufferedImage image = null;
String captchaType = captchaProperties.getType();
// 生成验证码
if ("math".equals(captchaType))
{
String capText = captchaProducerMath.createText();
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@") + 1);
image = captchaProducerMath.createImage(capStr);
}
else if ("char".equals(captchaType))
{
capStr = code = captchaProducer.createText();
image = captchaProducer.createImage(capStr);
}
redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
// 转换流信息写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try
{
ImageIO.write(image, "jpg", os);
}
catch (IOException e)
{
return AjaxResult.error(e.getMessage());
}
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray()));
return ajax;
}
/**
* 校验验证码
*/
@Override
public void checkCaptcha(String code, String uuid) throws CaptchaException
{
if (StringUtils.isEmpty(code))
{
throw new CaptchaException("验证码不能为空");
}
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
String captcha = redisService.getCacheObject(verifyKey);
if (captcha == null)
{
throw new CaptchaException("验证码已失效");
}
redisService.deleteObject(verifyKey);
if (!code.equalsIgnoreCase(captcha))
{
throw new CaptchaException("验证码错误");
}
}
}
package com.matchtech.gateway.utils;
import com.alibaba.fastjson2.JSON;
import com.matchtech.common.core.constants.Constants;
import com.matchtech.common.core.domain.R;
import com.matchtech.common.core.utils.StringUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import reactor.core.publisher.Mono;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class WebFluxUtil {
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value)
{
return webFluxResponseWriter(response, HttpStatus.OK, value, R.FAIL);
}
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param code 响应状态码
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code)
{
return webFluxResponseWriter(response, HttpStatus.OK, value, code);
}
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param status http状态码
* @param code 响应状态码
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code)
{
return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
}
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param contentType content-type
* @param status http状态码
* @param code 响应状态码
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code)
{
response.setStatusCode(status);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
R<?> result = R.fail(code, value.toString());
DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
return response.writeWith(Mono.just(dataBuffer));
}
/**
* 内容解码
*
* @param str 内容
* @return 解码后的内容
*/
public static String urlDecode(String str)
{
try
{
return URLDecoder.decode(str, Constants.UTF8);
}
catch (UnsupportedEncodingException e)
{
return StringUtils.EMPTY;
}
}
/**
* 内容编码
*
* @param str 内容
* @return 编码后的内容
*/
public static String urlEncode(String str)
{
try
{
return URLEncoder.encode(str, Constants.UTF8);
}
catch (UnsupportedEncodingException e)
{
return StringUtils.EMPTY;
}
}
}
Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}
_ _
(_) | |
_ __ _ _ ___ _ _ _ ______ __ _ __ _ | |_ ___ __ __ __ _ _ _
| '__|| | | | / _ \ | | | || ||______| / _` | / _` || __| / _ \\ \ /\ / / / _` || | | |
| | | |_| || (_) || |_| || | | (_| || (_| || |_ | __/ \ V V / | (_| || |_| |
|_| \__,_| \___/ \__, ||_| \__, | \__,_| \__| \___| \_/\_/ \__,_| \__, |
__/ | __/ | __/ |
|___/ |___/ |___/
\ No newline at end of file
......@@ -3,7 +3,7 @@ server:
port: 9999
# Spring
spring:
spring:
application:
# 应用名称
name: matchtech-gateway
......@@ -26,6 +26,56 @@ spring:
extension-configs:
- data-id: matchtech-gateway-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
refresh: true
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
routes:
# 认证中心
- id: matchtech-auth
uri: lb://matchtech-auth
predicates:
- Path=/auth/**
filters:
- CacheRequestBody
- StripPrefix=1
- id: matchtech-auth-local
uri: lb://matchtech-auth-local
predicates:
- Path=/auth-local/**
filters:
# 验证码处理
- CacheRequestBody
- StripPrefix=1
# 代码生成
- id: matchtech-gen
uri: lb://matchtech-gen
predicates:
- Path=/code/**
filters:
- StripPrefix=1
# 定时任务
- id: matchtech-job
uri: lb://matchtech-job
predicates:
- Path=/schedule/**
filters:
- StripPrefix=1
# 系统模块
- id: matchtech-system
uri: lb://matchtech-system
predicates:
- Path=/system/**
filters:
- StripPrefix=1
# 文件服务
- id: matchtech-file
uri: lb://matchtech-file
predicates:
- Path=/file/**
filters:
- StripPrefix=1
sentinel:
# 取消控制台懒加载
eager: true
......@@ -41,3 +91,23 @@ spring:
groupId: DEFAULT_GROUP
data-type: json
rule-type: gw-flow
data:
redis:
host: localhost
port: 6379
# password:
redis:
host: localhost
port: 6379
# password:
knife4j:
gateway:
enabled: true
strategy: discover #网关文档聚合方式:服务发现(自动聚合)
discover:
version: openapi3 #配置OpenAPI3规范
enabled: true #配置开启服务发现
tags-sorter: order #配置tag排序规则
operations-sorter: order #配置operation排序规则
\ No newline at end of file
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-modules</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-modules</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -66,11 +66,6 @@
</dependency>
<!-- matchtech Common Swagger -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
</dependencies>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-modules</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......@@ -64,15 +64,6 @@
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common-log</artifactId>
</dependency>
<!-- matchtech Common Swagger -->
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.matchtech</groupId>
......@@ -105,8 +96,19 @@
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<dependency>
<groupId>com.matchtech</groupId>
<artifactId>matchtech-common-mybatis</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
......
package com.matchtech.system;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import com.matchtech.common.security.annotation.EnableCustomConfig;
import com.matchtech.common.security.annotation.EnableRyFeignClients;
......@@ -17,6 +19,7 @@ public class MatchtechSystemApplication
{
public static void main(String[] args)
{
// 设置系统属性解决factoryBeanObjectType类型问题
SpringApplication.run(MatchtechSystemApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 系统模块启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
......@@ -29,4 +32,4 @@ public class MatchtechSystemApplication
" | | \\ / \\ / \n" +
" ''-' `'-' `-..-' ");
}
}
}
\ No newline at end of file
......@@ -12,6 +12,7 @@ import com.matchtech.system.api.model.LoginUser;
import com.matchtech.system.entity.SysUserEntity;
import com.matchtech.system.service.SysUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
......@@ -31,6 +32,7 @@ public class SysUserController extends BaseController {
@Operation(summary = "用户详情")
@PostMapping("/detail")
@Schema(description = "用户详情")
public ApiResponseEntity<SysUserEntity> detail(@RequestBody BaseIdDto baseIdDto) {
return ApiResponseUtils.success(sysUserService.getById(baseIdDto.getId()));
}
......
package com.matchtech.system.doc;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @auther macrozheng
* @description SpringDoc API文档相关配置
* @date 2023/11/29
* @github https://github.com/macrozheng
*/
@Configuration
public class SpringDocConfig {
private static final String SECURITY_SCHEME_NAME = "BearerAuth";
@Bean
public OpenAPI mallTinyOpenAPI() {
return new OpenAPI()
.info(new Info().title("micro-knife4j-order API")
.description("micro-knife4j-order API 演示")
.version("v1.0.0")
.license(new License().name("Apache 2.0").url("https://github.com/macrozheng/mall-learning")))
.externalDocs(new ExternalDocumentation()
.description("SpringBoot实战电商项目mall(60K+Star)全套文档")
.url("http://www.macrozheng.com"))
.addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME))
.components(new Components()
.addSecuritySchemes(SECURITY_SCHEME_NAME,
new SecurityScheme()
.name(SECURITY_SCHEME_NAME)
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")));
}
}
package com.matchtech.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.matchtech.common.mybatis.BaseMapperX;
import com.matchtech.system.entity.SysEmployeeEntity;
import org.apache.ibatis.annotations.Mapper;
......
......@@ -28,19 +28,21 @@ spring:
- data-id: matchtech-system-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
refresh: true
# springdoc配置
management:
endpoints:
web:
exposure:
include: "*"
springdoc:
gatewayUrl: http://localhost:9999/${spring.application.name}
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
show-extensions: true
api-docs:
# 是否开启接口文档
enabled: true
info:
# 标题
title: '系统模块接口文档'
# 描述
description: '系统模块接口描述'
# 作者信息
contact:
name: matchtech
url: ${spring.application.name}
\ No newline at end of file
path: /v3/api-docs
group-configs:
- group: 'default'
paths-to-match: '/order/**'
packages-to-scan: com.macro.cloud.controller
default-flat-param-object: false
\ No newline at end of file
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.matchtech</groupId>
<artifactId>rapidplatform-cloud</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
</parent>
<dependencies>
<dependency>
......
......@@ -6,33 +6,36 @@
<groupId>com.matchtech</groupId>
<artifactId>rapidplatform-cloud</artifactId>
<version>3.1.1</version>
<version>3.2.2</version>
<name>rapidplatform-cloud</name>
<description>微服务系统</description>
<properties>
<matchtech.version>3.1.1</matchtech.version>
<matchtech.version>3.2.2</matchtech.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<!-- Spring生态核心版本 -->
<spring-boot.version>3.1.1</spring-boot.version>
<spring-cloud.version>2022.0.4</spring-cloud.version>
<spring-cloud-alibaba.version>2022.0.0.2</spring-cloud-alibaba.version>
<spring-boot-admin.version>3.1.1</spring-boot-admin.version>
<spring-boot.version>3.2.2</spring-boot.version>
<spring-cloud.version>2023.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version>
<!-- 数据访问层版本 -->
<mybatis-plus-boot-starter.version>3.5.3.1</mybatis-plus-boot-starter.version>
<druid.version>1.2.20</druid.version>
<mybatis-spring-boot-starter-test.version>4.0.0</mybatis-spring-boot-starter-test.version>
<!-- 更新MyBatis相关版本 -->
<mybatis-spring.version>3.0.3</mybatis-spring.version>
<mybatis.version>3.5.16</mybatis.version>
<mybatis-plus-boot-starter.version>3.5.7</mybatis-plus-boot-starter.version>
<!-- 更新pagehelper版本 -->
<pagehelper.boot.version>2.1.0</pagehelper.boot.version>
<!-- 更新Druid版本 -->
<druid.version>1.2.23</druid.version>
<dynamic-ds.version>4.1.3</dynamic-ds.version>
<pagehelper.boot.version>1.4.6</pagehelper.boot.version>
<!-- 开发工具和工具类版本 -->
<mapstruct.version>1.5.3.Final</mapstruct.version>
<kaptcha.version>2.3.3</kaptcha.version>
<knife4j-spring-boot.version>4.1.0</knife4j-spring-boot.version>
<springdoc.version>2.2.0</springdoc.version>
<knife4j.version>4.5.0</knife4j.version>
<!-- 其他工具版本 -->
<commons.io.version>2.11.0</commons.io.version>
......@@ -46,6 +49,7 @@
<velocity.version>2.3</velocity.version>
<minio.version>8.4.5</minio.version>
<ojdbc11.version>21.7.0.0</ojdbc11.version>
<hutool.version>5.8.20</hutool.version>
</properties>
<!-- 依赖声明 -->
......@@ -53,6 +57,25 @@
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
......@@ -90,15 +113,6 @@
<scope>import</scope>
</dependency>
<!-- SpringCloud Alibaba 微服务 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringBoot 依赖配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
......@@ -108,20 +122,6 @@
<scope>import</scope>
</dependency>
<!-- Springdoc webmvc 依赖配置 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- 验证码 -->
<dependency>
<groupId>pro.fessional</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
......@@ -153,6 +153,14 @@
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<!-- MyBatis 测试 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>${mybatis-spring-boot-starter-test.version}</version>
<scope>test</scope>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
......@@ -297,6 +305,12 @@
<version>${matchtech.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
......@@ -315,6 +329,31 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
......
2025年12月29日
2025年12月29日
2025年12月29日
springboot 降版本到3.1.1,其他也降版本
springboot 降版本到3.2.2,其他也降版本
2025年12月30日
账号密码登录认证
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment