From 5193598bb4e12602decb29515e3a2ef1861b002f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=98=BF=E6=98=8E?= <1763113879@qq.com>
Date: Thu, 16 Nov 2023 13:19:18 +0800
Subject: [PATCH] =?UTF-8?q?=E5=8D=95=E4=BD=93=E9=A1=B9=E7=9B=AE=E7=99=BB?=
=?UTF-8?q?=E5=BD=95=E6=88=90=E5=8A=9F=EF=BC=9B=E5=85=A8=E5=B1=80=E5=BC=82?=
=?UTF-8?q?=E5=B8=B8=E5=BE=85=E5=A4=84=E7=90=86=EF=BC=9B=E5=9F=BA=E6=9C=AC?=
=?UTF-8?q?=E5=8F=AF=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ruoyi/system/api/RemoteFileService.java | 2 +-
.../ruoyi/system/api/RemoteLogService.java | 2 +-
.../ruoyi/system/api/RemoteUserService.java | 2 +-
ruoyi-web/ruoyi-web-admin/pom.xml | 8 +-
.../ruoyi/web/admin/config/CaptchaConfig.java | 99 ++++++++++++
.../config/CustomHttpServletRequest.java | 58 +++++++
.../web/admin/config/KaptchaTextCreator.java | 76 +++++++++
.../config/properties/CaptchaProperties.java | 45 ++++++
.../properties/IgnoreWhiteProperties.java | 33 ++++
.../config/properties/XssProperties.java | 48 ++++++
.../admin/controller/CaptchaController.java | 32 ++++
.../filter/JwtAuthenticationTokenFilter.java | 152 ++++++++++++++++++
.../admin/service/ValidateCodeService.java | 24 +++
.../service/impl/ValidateCodeServiceImpl.java | 122 ++++++++++++++
.../src/main/resources/application-local.yml | 92 +++++++----
.../src/main/resources/bootstrap.yml | 61 +------
.../src/main/resources/logback.xml | 2 +-
17 files changed, 762 insertions(+), 96 deletions(-)
create mode 100644 ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/CaptchaConfig.java
create mode 100644 ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/CustomHttpServletRequest.java
create mode 100644 ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/KaptchaTextCreator.java
create mode 100644 ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/CaptchaProperties.java
create mode 100644 ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/IgnoreWhiteProperties.java
create mode 100644 ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/XssProperties.java
create mode 100644 ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/controller/CaptchaController.java
create mode 100644 ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/filter/JwtAuthenticationTokenFilter.java
create mode 100644 ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/service/ValidateCodeService.java
create mode 100644 ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/service/impl/ValidateCodeServiceImpl.java
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteFileService.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteFileService.java
index ae56a54a..931f87e1 100644
--- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteFileService.java
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteFileService.java
@@ -15,7 +15,7 @@ import com.ruoyi.system.api.factory.RemoteFileFallbackFactory;
*
* @author ruoyi
*/
-@FeignClient(contextId = "remoteFileService", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class)
+@FeignClient(contextId = "remoteFileService",url = "${feign.debug.url.file:}", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class)
public interface RemoteFileService
{
/**
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteLogService.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteLogService.java
index 3402afad..51d3147f 100644
--- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteLogService.java
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteLogService.java
@@ -16,7 +16,7 @@ import com.ruoyi.system.api.factory.RemoteLogFallbackFactory;
*
* @author ruoyi
*/
-@FeignClient(contextId = "remoteLogService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteLogFallbackFactory.class)
+@FeignClient(contextId = "remoteLogService",url = "${feign.debug.url.system:}", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteLogFallbackFactory.class)
public interface RemoteLogService
{
/**
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java
index e7fe34c5..04ceb259 100644
--- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteUserService.java
@@ -18,7 +18,7 @@ import com.ruoyi.system.api.model.LoginUser;
*
* @author ruoyi
*/
-@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
+@FeignClient(contextId = "remoteUserService",url = "${feign.debug.url.system:}", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
public interface RemoteUserService
{
/**
diff --git a/ruoyi-web/ruoyi-web-admin/pom.xml b/ruoyi-web/ruoyi-web-admin/pom.xml
index af3c512e..24d00986 100644
--- a/ruoyi-web/ruoyi-web-admin/pom.xml
+++ b/ruoyi-web/ruoyi-web-admin/pom.xml
@@ -48,13 +48,11 @@
${ruoyi.version}
-
+
- com.ruoyi
- ruoyi-gateway
- ${ruoyi.version}
+ pro.fessional
+ kaptcha
-
\ No newline at end of file
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/CaptchaConfig.java b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/CaptchaConfig.java
new file mode 100644
index 00000000..0633e21d
--- /dev/null
+++ b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/CaptchaConfig.java
@@ -0,0 +1,99 @@
+package com.ruoyi.web.admin.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.KAPTCHA_BORDER;
+import static com.google.code.kaptcha.Constants.KAPTCHA_BORDER_COLOR;
+import static com.google.code.kaptcha.Constants.KAPTCHA_IMAGE_HEIGHT;
+import static com.google.code.kaptcha.Constants.KAPTCHA_IMAGE_WIDTH;
+import static com.google.code.kaptcha.Constants.KAPTCHA_NOISE_COLOR;
+import static com.google.code.kaptcha.Constants.KAPTCHA_NOISE_IMPL;
+import static com.google.code.kaptcha.Constants.KAPTCHA_OBSCURIFICATOR_IMPL;
+import static com.google.code.kaptcha.Constants.KAPTCHA_SESSION_CONFIG_KEY;
+import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH;
+import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE;
+import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR;
+import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES;
+import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE;
+import static com.google.code.kaptcha.Constants.KAPTCHA_TEXTPRODUCER_IMPL;
+
+/**
+ * 验证码配置
+ *
+ * @author ruoyi
+ */
+@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.web.admin.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
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/CustomHttpServletRequest.java b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/CustomHttpServletRequest.java
new file mode 100644
index 00000000..7a9e5b21
--- /dev/null
+++ b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/CustomHttpServletRequest.java
@@ -0,0 +1,58 @@
+package com.ruoyi.web.admin.config;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+/**
+ * @author 1763113879@qq.com
+ * @version V2.1
+ * @since 2.1.0 2023/11/16 13:07
+ */
+public class CustomHttpServletRequest extends HttpServletRequestWrapper {
+
+ private Map headers=new HashMap<>();
+
+ public CustomHttpServletRequest(HttpServletRequest request){
+ super(request);
+ }
+
+ public void addHeader(String name,String value){
+ headers.put(name, value);
+ }
+
+ @Override
+ public String getHeader(String name) {
+ String value=super.getHeader(name);
+
+ if (headers.containsKey(name)){
+ value=headers.get(name);
+ }
+
+ return value;
+ }
+
+ @Override
+ public Enumeration getHeaderNames() {
+ List names= Collections.list(super.getHeaderNames());
+ names.addAll(headers.keySet());
+
+ return Collections.enumeration(names);
+ }
+
+ @Override
+ public Enumeration getHeaders(String name) {
+ List list= Collections.list(super.getHeaders(name));
+
+ if (headers.containsKey(name)){
+ list.add(headers.get(name));
+ }
+
+ return Collections.enumeration(list);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/KaptchaTextCreator.java b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/KaptchaTextCreator.java
new file mode 100644
index 00000000..aba30e8c
--- /dev/null
+++ b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/KaptchaTextCreator.java
@@ -0,0 +1,76 @@
+package com.ruoyi.web.admin.config;
+
+import java.util.Random;
+
+import com.google.code.kaptcha.text.impl.DefaultTextCreator;
+
+/**
+ * 验证码文本生成器
+ *
+ * @author ruoyi
+ */
+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
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/CaptchaProperties.java b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/CaptchaProperties.java
new file mode 100644
index 00000000..1259eca0
--- /dev/null
+++ b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/CaptchaProperties.java
@@ -0,0 +1,45 @@
+package com.ruoyi.web.admin.config.properties;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 验证码配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+@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;
+ }
+}
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/IgnoreWhiteProperties.java b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/IgnoreWhiteProperties.java
new file mode 100644
index 00000000..03e4c5d2
--- /dev/null
+++ b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/IgnoreWhiteProperties.java
@@ -0,0 +1,33 @@
+package com.ruoyi.web.admin.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 ruoyi
+ */
+@Configuration
+@ConfigurationProperties(prefix = "security.ignore")
+public class IgnoreWhiteProperties
+{
+ /**
+ * 放行白名单配置,网关不校验此处的白名单
+ */
+ private List whites = new ArrayList<>();
+
+ public List getWhites()
+ {
+ return whites;
+ }
+
+ public void setWhites(List whites)
+ {
+ this.whites = whites;
+ }
+}
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/XssProperties.java b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/XssProperties.java
new file mode 100644
index 00000000..fa713a24
--- /dev/null
+++ b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/config/properties/XssProperties.java
@@ -0,0 +1,48 @@
+package com.ruoyi.web.admin.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 ruoyi
+ */
+@Configuration
+@ConfigurationProperties(prefix = "security.xss")
+public class XssProperties
+{
+ /**
+ * Xss开关
+ */
+ private Boolean enabled;
+
+ /**
+ * 排除路径
+ */
+ private List excludeUrls = new ArrayList<>();
+
+ public Boolean getEnabled()
+ {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ public List getExcludeUrls()
+ {
+ return excludeUrls;
+ }
+
+ public void setExcludeUrls(List excludeUrls)
+ {
+ this.excludeUrls = excludeUrls;
+ }
+}
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/controller/CaptchaController.java b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/controller/CaptchaController.java
new file mode 100644
index 00000000..728c9e6b
--- /dev/null
+++ b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/controller/CaptchaController.java
@@ -0,0 +1,32 @@
+package com.ruoyi.web.admin.controller;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.web.admin.service.ValidateCodeService;
+
+/**
+ * 验证码操作处理
+ *
+ * @author ruoyi
+ */
+@RestController
+public class CaptchaController {
+
+ @Autowired
+ private ValidateCodeService validateCodeService;
+
+ /**
+ * 生成验证码
+ */
+ @GetMapping("/code")
+ public AjaxResult getCode(HttpServletResponse response) throws IOException {
+ return validateCodeService.createCaptcha();
+ }
+}
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/filter/JwtAuthenticationTokenFilter.java b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/filter/JwtAuthenticationTokenFilter.java
new file mode 100644
index 00000000..fa16c1dc
--- /dev/null
+++ b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/filter/JwtAuthenticationTokenFilter.java
@@ -0,0 +1,152 @@
+package com.ruoyi.web.admin.filter;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import com.ruoyi.common.core.constant.CacheConstants;
+import com.ruoyi.common.core.constant.SecurityConstants;
+import com.ruoyi.common.core.constant.TokenConstants;
+import com.ruoyi.common.core.context.SecurityContextHolder;
+import com.ruoyi.common.core.exception.InnerAuthException;
+import com.ruoyi.common.core.exception.ServiceException;
+import com.ruoyi.common.core.utils.JwtUtils;
+import com.ruoyi.common.core.utils.ServletUtils;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.redis.service.RedisService;
+import com.ruoyi.common.security.auth.AuthUtil;
+import com.ruoyi.common.security.service.TokenService;
+import com.ruoyi.system.api.model.LoginUser;
+import com.ruoyi.web.admin.config.CustomHttpServletRequest;
+import com.ruoyi.web.admin.config.properties.IgnoreWhiteProperties;
+
+import io.jsonwebtoken.Claims;
+
+/**
+ * token过滤器 验证token有效性
+ *
+ * @author ruoyi
+ */
+@Component
+@Order(-200)
+public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
+
+ private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
+
+ // 排除过滤的 uri 地址,nacos自行添加
+ @Autowired
+ private IgnoreWhiteProperties ignoreWhite;
+
+ @Autowired
+ private RedisService redisService;
+
+ @Autowired
+ private TokenService tokenService;
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
+ throws ServletException, IOException {
+
+ String source = request.getHeader(SecurityConstants.FROM_SOURCE);
+ // 内部请求验证 不拦截
+ if (StringUtils.equals(SecurityConstants.INNER, source)) {
+ chain.doFilter(request, response);
+ return;
+ }
+
+ String requestURI = request.getRequestURI();
+ // 跳过不需要验证的路径
+ if (!StringUtils.matches(requestURI, ignoreWhite.getWhites())) {
+
+ String token = getToken(request);
+ if (StringUtils.isEmpty(token)) {
+ throw new ServiceException("令牌不能为空");
+
+ }
+ Claims claims = JwtUtils.parseToken(token);
+ if (claims == null) {
+ throw new ServiceException("令牌已过期或验证不正确");
+
+ }
+ String userkey = JwtUtils.getUserKey(claims);
+ boolean islogin = redisService.hasKey(getTokenKey(userkey));
+ if (!islogin) {
+ throw new ServiceException("登录状态已过期");
+
+ }
+ String userid = JwtUtils.getUserId(claims);
+ String username = JwtUtils.getUserName(claims);
+ if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) {
+ throw new ServiceException("令牌验证失败");
+
+ }
+
+ SecurityContextHolder.setUserId(userid);
+ SecurityContextHolder.setUserName(username);
+ SecurityContextHolder.setUserKey(userkey);
+
+ CustomHttpServletRequest customHttpServletRequest = new CustomHttpServletRequest(request);
+ // 设置用户信息到请求
+ addHeader(customHttpServletRequest, SecurityConstants.USER_KEY, userkey);
+ addHeader(customHttpServletRequest, SecurityConstants.DETAILS_USER_ID, userid);
+ addHeader(customHttpServletRequest, SecurityConstants.DETAILS_USERNAME, username);
+ //转换为自定义的request
+ request = customHttpServletRequest;
+
+ if (StringUtils.isNotEmpty(token)) {
+ LoginUser loginUser = AuthUtil.getLoginUser(token);
+ if (StringUtils.isNotNull(loginUser)) {
+ AuthUtil.verifyLoginUserExpire(loginUser);
+ SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser);
+ }
+ }
+
+ }
+
+ chain.doFilter(request, response);
+ }
+
+ /**
+ * 获取缓存key
+ */
+ private String getTokenKey(String token) {
+ return CacheConstants.LOGIN_TOKEN_KEY + token;
+ }
+
+ /**
+ * 获取请求token
+ */
+ private String getToken(HttpServletRequest request) {
+ String token = request.getHeader(TokenConstants.AUTHENTICATION);
+ // 如果前端设置了令牌前缀,则裁剪掉前缀
+ if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {
+ token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
+ }
+ return token;
+ }
+
+ private void addHeader(CustomHttpServletRequest customHttpServletRequest, String name, Object value)
+ {
+ if (value == null)
+ {
+ return;
+ }
+ String valueStr = value.toString();
+ String valueEncode = ServletUtils.urlEncode(valueStr);
+ customHttpServletRequest.addHeader(name, valueEncode);
+ }
+
+
+
+}
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/service/ValidateCodeService.java b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/service/ValidateCodeService.java
new file mode 100644
index 00000000..c11713f4
--- /dev/null
+++ b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/service/ValidateCodeService.java
@@ -0,0 +1,24 @@
+package com.ruoyi.web.admin.service;
+
+import java.io.IOException;
+
+import com.ruoyi.common.core.exception.CaptchaException;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+
+/**
+ * 验证码处理
+ *
+ * @author ruoyi
+ */
+public interface ValidateCodeService
+{
+ /**
+ * 生成验证码
+ */
+ public AjaxResult createCaptcha() throws IOException, CaptchaException;
+
+ /**
+ * 校验验证码
+ */
+ public void checkCaptcha(String key, String value) throws CaptchaException;
+}
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/service/impl/ValidateCodeServiceImpl.java b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/service/impl/ValidateCodeServiceImpl.java
new file mode 100644
index 00000000..b4733070
--- /dev/null
+++ b/ruoyi-web/ruoyi-web-admin/src/main/java/com/ruoyi/web/admin/service/impl/ValidateCodeServiceImpl.java
@@ -0,0 +1,122 @@
+package com.ruoyi.web.admin.service.impl;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import javax.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.ruoyi.common.core.constant.CacheConstants;
+import com.ruoyi.common.core.constant.Constants;
+import com.ruoyi.common.core.exception.CaptchaException;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.core.utils.sign.Base64;
+import com.ruoyi.common.core.utils.uuid.IdUtils;
+import com.ruoyi.common.core.web.domain.AjaxResult;
+import com.ruoyi.common.redis.service.RedisService;
+import com.ruoyi.web.admin.config.properties.CaptchaProperties;
+import com.ruoyi.web.admin.service.ValidateCodeService;
+
+/**
+ * 验证码实现处理
+ *
+ * @author ruoyi
+ */
+@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("验证码不能为空");
+ }
+ if (StringUtils.isEmpty(uuid))
+ {
+ throw new CaptchaException("验证码已失效");
+ }
+ String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
+ String captcha = redisService.getCacheObject(verifyKey);
+ redisService.deleteObject(verifyKey);
+
+ if (!code.equalsIgnoreCase(captcha))
+ {
+ throw new CaptchaException("验证码错误");
+ }
+ }
+}
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/resources/application-local.yml b/ruoyi-web/ruoyi-web-admin/src/main/resources/application-local.yml
index bd46ef14..775aff3c 100644
--- a/ruoyi-web/ruoyi-web-admin/src/main/resources/application-local.yml
+++ b/ruoyi-web/ruoyi-web-admin/src/main/resources/application-local.yml
@@ -151,20 +151,11 @@ mybatis:
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
#configLocation: classpath:mybatis/mybatis-config.xml
-
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
-
-# Swagger配置
-swagger:
- # 是否开启swagger
- enabled: true
- # 请求前缀
- pathMapping: /dev-api
-
# 防止XSS攻击
xss:
# 过滤开关
@@ -173,47 +164,80 @@ xss:
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
-
-
-# 安全配置
-security:
- # 验证码
- captcha:
- enabled: true
- type: math
- # 防止XSS攻击
- xss:
- enabled: true
- excludeUrls:
- - /system/notice
- # 不校验白名单
- ignore:
- whites:
- - /auth/logout
- - /auth/login
- - /auth/register
- - /*/v2/api-docs
- - /csrf
-
##################### 文件模块 ###########################################
# 本地文件上传
file:
domain: http://127.0.0.1:9300
path: ruoyi/uploadPath
prefix: /statics
-
# FastDFS配置
fdfs:
domain: http://8.129.231.12
soTimeout: 3000
connectTimeout: 2000
trackerList: 8.129.231.12:22122
-
# Minio配置
minio:
url: http://8.129.231.12:9000
accessKey: minioadmin
secretKey: minioadmin
bucketName: test
-
######################## 文件模块 ########################################
+# feign 配置
+feign:
+ sentinel:
+ enabled: true
+ okhttp:
+ enabled: true
+ httpclient:
+ enabled: false
+ client:
+ config:
+ default:
+ connectTimeout: 10000
+ readTimeout: 10000
+ compression:
+ request:
+ enabled: true
+ response:
+ enabled: true
+ # 设置feign本地调用地址
+ debug:
+ url:
+ system: http://localhost:${server.port}/system
+ file: http://localhost:${server.port}/file
+# 暴露监控端点
+management:
+ endpoints:
+ web:
+ exposure:
+ include: '*'
+# 安全配置
+security:
+ # 验证码
+ captcha:
+ enabled: true
+ type: math
+ # 防止XSS攻击
+ xss:
+ enabled: true
+ excludeUrls:
+ - /system/notice
+ # 不校验白名单
+ ignore:
+ whites:
+ - /auth/logout
+ - /auth/login
+ - /auth/register
+ - /*/v2/api-docs
+ - /csrf
+ - /code
+# Swagger配置
+swagger:
+ # 是否开启swagger
+ enabled: true
+ # 请求前缀
+ pathMapping: /dev-api
+ title: 系统模块接口文档
+ license: Powered By ruoyi
+ licenseUrl: https://ruoyi.vip
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/resources/bootstrap.yml b/ruoyi-web/ruoyi-web-admin/src/main/resources/bootstrap.yml
index 5a2ccaaa..605addad 100644
--- a/ruoyi-web/ruoyi-web-admin/src/main/resources/bootstrap.yml
+++ b/ruoyi-web/ruoyi-web-admin/src/main/resources/bootstrap.yml
@@ -23,57 +23,12 @@ spring:
locator:
lowerCaseServiceId: true
enabled: true
+ discovery:
+ client:
+ simple:
+ instances:
+ ruoyi-file:
+ - uri: http://localhost:8080
+ ruoyi-system:
+ - uri: http://localhost:9090
-# feign 配置
-feign:
- sentinel:
- enabled: true
- okhttp:
- enabled: true
- httpclient:
- enabled: false
- client:
- config:
- default:
- connectTimeout: 10000
- readTimeout: 10000
- compression:
- request:
- enabled: true
- response:
- enabled: true
-
-# 暴露监控端点
-management:
- endpoints:
- web:
- exposure:
- include: '*'
-
-
-
-# 安全配置
-security:
- # 验证码
- captcha:
- enabled: true
- type: math
- # 防止XSS攻击
- xss:
- enabled: true
- excludeUrls:
- - /system/notice
- # 不校验白名单
- ignore:
- whites:
- - /auth/logout
- - /auth/login
- - /auth/register
- - /*/v2/api-docs
- - /csrf
-
-# swagger配置
-swagger:
- title: 系统模块接口文档
- license: Powered By ruoyi
- licenseUrl: https://ruoyi.vip
\ No newline at end of file
diff --git a/ruoyi-web/ruoyi-web-admin/src/main/resources/logback.xml b/ruoyi-web/ruoyi-web-admin/src/main/resources/logback.xml
index e50a6bb1..a987a9af 100644
--- a/ruoyi-web/ruoyi-web-admin/src/main/resources/logback.xml
+++ b/ruoyi-web/ruoyi-web-admin/src/main/resources/logback.xml
@@ -3,7 +3,7 @@
-
+