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 @@ - +