Conflicts:
	ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/GenConstants.java
	ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ExceptionUtil.java
	ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ServletUtils.java
	ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/file/ImageUtils.java
	ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/poi/ExcelUtil.java
	ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/sql/SqlUtil.java
	ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/xss/XssValidator.java
	ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/DictUtils.java
	ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/BlackListUrlFilter.java
	ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/ValidateCodeFilter.java
	ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/XssFilter.java
	ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/ValidateCodeHandler.java
	ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/ValidateCodeService.java
	ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java
	ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/utils/FileUploadUtils.java
	ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableServiceImpl.java
	ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityUtils.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java
	ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
	ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml
	ruoyi-ui/src/components/FileUpload/index.vue
	ruoyi-ui/src/router/index.js
This commit is contained in:
疯狂的狮子li
2022-02-14 14:55:55 +08:00
36 changed files with 477 additions and 473 deletions

View File

@@ -1,58 +1,58 @@
package com.ruoyi.gateway.filter;
import com.ruoyi.gateway.utils.WebFluxUtils;
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 ruoyi
*/
@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 WebFluxUtils.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() ? false : blacklistUrlPattern.stream().filter(p -> p.matcher(url).find()).findAny().isPresent();
}
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.ruoyi.gateway.filter;
import com.ruoyi.gateway.utils.WebFluxUtils;
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 ruoyi
*/
@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 WebFluxUtils.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));
});
}
}
}

View File

@@ -1,73 +1,73 @@
package com.ruoyi.gateway.filter;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.gateway.config.properties.CaptchaProperties;
import com.ruoyi.gateway.service.ValidateCodeService;
import com.ruoyi.gateway.utils.WebFluxUtils;
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.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* 验证码过滤器
*
* @author ruoyi
*/
@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.containsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled()) {
return chain.filter(exchange);
}
try {
String rspStr = resolveBodyFromRequest(request);
Map<String, String> obj = JsonUtils.parseMap(rspStr);
validateCodeService.checkCapcha(obj.get(CODE), obj.get(UUID));
} catch (Exception e) {
return WebFluxUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage());
}
return chain.filter(exchange);
};
}
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.ruoyi.gateway.filter;
import com.ruoyi.common.core.utils.JsonUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.gateway.config.properties.CaptchaProperties;
import com.ruoyi.gateway.service.ValidateCodeService;
import com.ruoyi.gateway.utils.WebFluxUtils;
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.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* 验证码过滤器
*
* @author ruoyi
*/
@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.containsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled()) {
return chain.filter(exchange);
}
try {
String rspStr = resolveBodyFromRequest(request);
Map<String, String> obj = JsonUtils.parseMap(rspStr);
validateCodeService.checkCaptcha(obj.get(CODE), obj.get(UUID));
} catch (Exception e) {
return WebFluxUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage());
}
return chain.filter(exchange);
};
}
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();
}
}

View File

@@ -1,110 +1,110 @@
package com.ruoyi.gateway.filter;
import cn.hutool.http.HtmlUtil;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.gateway.config.properties.XssProperties;
import io.netty.buffer.ByteBufAllocator;
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.*;
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 reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* 跨站脚本过滤器
*
* @author ruoyi
*/
@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();
// GET DELETE 不过滤
HttpMethod method = request.getMethod();
if (method == null || method.matches("GET") || method.matches("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 = HtmlUtil.cleanHtmlTag(bodyStr);
// 转成字节
byte[] bytes = bodyStr.getBytes();
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 request
*/
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.ruoyi.gateway.filter;
import cn.hutool.http.HtmlUtil;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.gateway.config.properties.XssProperties;
import io.netty.buffer.ByteBufAllocator;
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.*;
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 reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* 跨站脚本过滤器
*
* @author ruoyi
*/
@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();
// GET DELETE 不过滤
HttpMethod method = request.getMethod();
if (method == null || method.matches("GET") || method.matches("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 = HtmlUtil.cleanHtmlTag(bodyStr);
// 转成字节
byte[] bytes = bodyStr.getBytes();
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;
}
}

View File

@@ -29,7 +29,7 @@ public class ValidateCodeHandler implements HandlerFunction<ServerResponse> {
public Mono<ServerResponse> handle(ServerRequest serverRequest) {
R<Map<String, Object>> ajax;
try {
ajax = validateCodeService.createCapcha();
ajax = validateCodeService.createCaptcha();
} catch (CaptchaException | IOException e) {
return Mono.error(e);
}

View File

@@ -15,10 +15,10 @@ public interface ValidateCodeService {
/**
* 生成验证码
*/
R<Map<String, Object>> createCapcha() throws IOException, CaptchaException;
R<Map<String, Object>> createCaptcha() throws IOException, CaptchaException;
/**
* 校验验证码
*/
void checkCapcha(String key, String value) throws CaptchaException;
void checkCaptcha(String key, String value) throws CaptchaException;
}

View File

@@ -36,7 +36,7 @@ public class ValidateCodeServiceImpl implements ValidateCodeService {
* 生成验证码
*/
@Override
public R<Map<String, Object>> createCapcha() throws IOException, CaptchaException {
public R<Map<String, Object>> createCaptcha() throws IOException, CaptchaException {
Map<String, Object> ajax = new HashMap<>();
boolean captchaOnOff = captchaProperties.getEnabled();
ajax.put("captchaOnOff", captchaOnOff);
@@ -83,7 +83,7 @@ public class ValidateCodeServiceImpl implements ValidateCodeService {
* 校验验证码
*/
@Override
public void checkCapcha(String code, String uuid) throws CaptchaException {
public void checkCaptcha(String code, String uuid) throws CaptchaException {
if (StringUtils.isEmpty(code)) {
throw new CaptchaException("验证码不能为空");
}