save code
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>digital-common</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.2.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>digital-config</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!--jeecg-tools-->
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-base-tools</artifactId>
|
||||
</dependency>
|
||||
<!--集成springmvc框架并实现自动配置 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!-- websocket -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<!--springboot2.3+ 需引入validation对应的包-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-starter-validation</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<!--springboot2.6+解决metrics端点不显示jvm信息的问题-->
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- commons -->
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${commons.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>${commons.version}</version>
|
||||
</dependency>
|
||||
<!-- freemarker -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- mybatis-plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- druid -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
<version>${druid.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 动态数据源 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
|
||||
<version>${dynamic-datasource-spring-boot-starter.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库驱动 -->
|
||||
<!--mysql-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql-connector-java.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- sqlserver-->
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>sqljdbc4</artifactId>
|
||||
<version>${sqljdbc4.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- oracle驱动 -->
|
||||
<dependency>
|
||||
<groupId>com.oracle</groupId>
|
||||
<artifactId>ojdbc6</artifactId>
|
||||
<version>${ojdbc6.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- postgresql驱动 -->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>${postgresql.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!--JWT-->
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
<version>${java-jwt.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--shiro-->
|
||||
<dependency>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-spring-boot-starter</artifactId>
|
||||
<version>${shiro.version}</version>
|
||||
</dependency>
|
||||
<!-- shiro-redis -->
|
||||
<dependency>
|
||||
<groupId>org.crazycake</groupId>
|
||||
<artifactId>shiro-redis</artifactId>
|
||||
<version>${shiro-redis.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-core</artifactId>
|
||||
</exclusion>
|
||||
<!-- [issues/3596] 解决启动报错:Cannot resolve com.sun:tools:1.8.0 -->
|
||||
<exclusion>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- knife4j -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
||||
<version>${knife4j-spring-boot-starter.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 代码生成器 -->
|
||||
<!-- 如下载失败,请参考此文档 http://doc.jeecg.com/2043876 -->
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>codegenerate</artifactId>
|
||||
<version>${codegenerate.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- AutoPoi Excel工具类-->
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework</groupId>
|
||||
<artifactId>autopoi-web</artifactId>
|
||||
<version>${autopoi-web.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- mini文件存储服务 -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 阿里云短信 -->
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
|
||||
<version>${aliyun-java-sdk-dysmsapi.version}</version>
|
||||
</dependency>
|
||||
<!-- aliyun oss -->
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>${aliyun.oss.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 解决okhttp引用了kotlin,应用启动有警告日志问题 -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-kotlin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>digital-base</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>digital-util</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
<version>1.9.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,28 @@
|
||||
package digital.config;
|
||||
|
||||
import org.jeecgframework.core.util.ApplicationContextUtil;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @Author: Scott
|
||||
* @Date: 2018/2/7
|
||||
* @description: autopoi 配置类
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
public class AutoPoiConfig {
|
||||
|
||||
/**
|
||||
* excel注解字典参数支持(导入导出字典值,自动翻译)
|
||||
* 举例: @Excel(name = "性别", width = 15, dicCode = "sex")
|
||||
* 1、导出的时候会根据字典配置,把值1,2翻译成:男、女;
|
||||
* 2、导入的时候,会把男、女翻译成1,2存进数据库;
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public ApplicationContextUtil applicationContextUtil() {
|
||||
return new org.jeecgframework.core.util.ApplicationContextUtil();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package digital.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import digital.util.util.SpringContextHolder;
|
||||
|
||||
/**
|
||||
* SpringContextHolder注册用
|
||||
*
|
||||
* @author: scott
|
||||
* @date: 2020/01/01 16:00
|
||||
*/
|
||||
@Configuration
|
||||
public class CommonConfig {
|
||||
|
||||
/**
|
||||
* Spring上下文工具配置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(SpringContextHolder.class)
|
||||
public SpringContextHolder springContextHolder() {
|
||||
SpringContextHolder holder = new SpringContextHolder();
|
||||
return holder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package digital.config;
|
||||
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import digital.base.constant.CommonConstant;
|
||||
|
||||
|
||||
/**
|
||||
* 跨域配置加载条件
|
||||
* @author: smcp
|
||||
*/
|
||||
public class CorsFilterCondition implements Condition {
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
Object object = context.getEnvironment().getProperty(CommonConstant.CLOUD_SERVER_KEY);
|
||||
//如果没有服务注册发现的配置 说明是单体应用 则加载跨域配置 返回true
|
||||
if(object==null){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package digital.config;
|
||||
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
|
||||
import com.alibaba.druid.util.Utils;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.servlet.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @Description: DruidConfig配置类
|
||||
* @author: smcp
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter(DruidDataSourceAutoConfigure.class)
|
||||
public class DruidConfig {
|
||||
|
||||
/**
|
||||
* 带有广告的common.js全路径,druid-1.1.14
|
||||
*/
|
||||
private static final String FILE_PATH = "support/http/resources/js/common.js";
|
||||
/**
|
||||
* 原始脚本,触发构建广告的语句
|
||||
*/
|
||||
private static final String ORIGIN_JS = "this.buildFooter();";
|
||||
/**
|
||||
* 替换后的脚本
|
||||
*/
|
||||
private static final String NEW_JS = "//this.buildFooter();";
|
||||
|
||||
/**
|
||||
* 去除Druid监控页面的广告
|
||||
*
|
||||
* @param properties DruidStatProperties属性集合
|
||||
* @return {@link FilterRegistrationBean}
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true")
|
||||
public FilterRegistrationBean<RemoveAdFilter> removeDruidAdFilter(
|
||||
DruidStatProperties properties) throws IOException {
|
||||
// 获取web监控页面的参数
|
||||
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
|
||||
// 提取common.js的配置路径
|
||||
String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
|
||||
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
|
||||
// 获取common.js
|
||||
String text = Utils.readFromResource(FILE_PATH);
|
||||
// 屏蔽 this.buildFooter(); 不构建广告
|
||||
final String newJs = text.replace(ORIGIN_JS, NEW_JS);
|
||||
FilterRegistrationBean<RemoveAdFilter> registration = new FilterRegistrationBean<>();
|
||||
registration.setFilter(new RemoveAdFilter(newJs));
|
||||
registration.addUrlPatterns(commonJsPattern);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除druid的广告过滤器
|
||||
*
|
||||
* @author BBF
|
||||
*/
|
||||
private class RemoveAdFilter implements Filter {
|
||||
|
||||
private final String newJs;
|
||||
|
||||
public RemoveAdFilter(String newJs) {
|
||||
this.newJs = newJs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
chain.doFilter(request, response);
|
||||
// 重置缓冲区,响应头不会被重置
|
||||
response.resetBuffer();
|
||||
response.getWriter().write(newJs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package digital.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import digital.config.vo.Shiro;
|
||||
|
||||
|
||||
/**
|
||||
* 加载项目配置
|
||||
* @author: smcp
|
||||
*/
|
||||
@Component("jeecgBaseConfig")
|
||||
@ConfigurationProperties(prefix = "jeecg")
|
||||
public class JeecgBaseConfig {
|
||||
/**
|
||||
* 是否启用安全模式
|
||||
*/
|
||||
private Boolean safeMode = false;
|
||||
/**
|
||||
* shiro拦截排除
|
||||
*/
|
||||
private Shiro shiro;
|
||||
/**
|
||||
* 签名密钥串(字典等敏感接口)
|
||||
* @TODO 降低使用成本加的默认值,实际以 yml配置 为准
|
||||
*/
|
||||
private String signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a";
|
||||
|
||||
public Boolean getSafeMode() {
|
||||
return safeMode;
|
||||
}
|
||||
|
||||
public void setSafeMode(Boolean safeMode) {
|
||||
this.safeMode = safeMode;
|
||||
}
|
||||
|
||||
public String getSignatureSecret() {
|
||||
return signatureSecret;
|
||||
}
|
||||
|
||||
public void setSignatureSecret(String signatureSecret) {
|
||||
this.signatureSecret = signatureSecret;
|
||||
}
|
||||
|
||||
public Shiro getShiro() {
|
||||
return shiro;
|
||||
}
|
||||
|
||||
public void setShiro(Shiro shiro) {
|
||||
this.shiro = shiro;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package digital.config;
|
||||
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import digital.base.constant.CommonConstant;
|
||||
|
||||
|
||||
/**
|
||||
* 微服务环境加载条件
|
||||
* @author: smcp
|
||||
*/
|
||||
public class JeecgCloudCondition implements Condition {
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
Object object = context.getEnvironment().getProperty(CommonConstant.CLOUD_SERVER_KEY);
|
||||
//如果没有服务注册发现的配置 说明是单体应用
|
||||
if(object==null){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package digital.config;
|
||||
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
import digital.util.util.oConvertUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* author: damao
|
||||
* date:2022-09-15 11:54
|
||||
* description:
|
||||
**/
|
||||
public class MyLocaleResolver implements LocaleResolver {
|
||||
@Override
|
||||
public Locale resolveLocale(HttpServletRequest req) {
|
||||
//获取请求中的参数链接
|
||||
String language = req.getHeader("langType");//前端传递的参数用来判断中文还是英文
|
||||
Locale locale = Locale.getDefault();//获取默认Locale
|
||||
if (!oConvertUtils.isEmpty(language)) {//判断传递参数是否为空
|
||||
locale = new Locale(language);
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package digital.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* 优雅的http请求方式RestTemplate
|
||||
* @author: smcp
|
||||
* @Return:
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
|
||||
return new RestTemplate(factory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
//ms毫秒
|
||||
factory.setReadTimeout(7200*1000);
|
||||
//ms毫秒
|
||||
factory.setConnectTimeout(7200*1000);
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//package digital.config;
|
||||
//
|
||||
//import lombok.Data;
|
||||
//import org.springframework.beans.factory.annotation.Value;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//
|
||||
///**
|
||||
// * 设置静态参数初始化
|
||||
// * @author: smcp
|
||||
// */
|
||||
//@Component
|
||||
//@Data
|
||||
//public class StaticConfig {
|
||||
//
|
||||
// @Value("${jeecg.oss.accessKey}")
|
||||
// private String accessKeyId;
|
||||
//
|
||||
// @Value("${jeecg.oss.secretKey}")
|
||||
// private String accessKeySecret;
|
||||
//
|
||||
// @Value(value = "${spring.mail.username}")
|
||||
// private String emailFrom;
|
||||
//
|
||||
//// /**
|
||||
//// * 签名密钥串
|
||||
//// */
|
||||
//// @Value(value = "${jeecg.signatureSecret}")
|
||||
//// private String signatureSecret;
|
||||
//
|
||||
//
|
||||
// /*@Bean
|
||||
// public void initStatic() {
|
||||
// DySmsHelper.setAccessKeyId(accessKeyId);
|
||||
// DySmsHelper.setAccessKeySecret(accessKeySecret);
|
||||
// EmailSendMsgHandle.setEmailFrom(emailFrom);
|
||||
// }*/
|
||||
//
|
||||
//}
|
||||
@@ -0,0 +1,184 @@
|
||||
package digital.config;
|
||||
|
||||
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
|
||||
import digital.base.constant.CommonConstant;
|
||||
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.ParameterBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.schema.ModelRef;
|
||||
import springfox.documentation.service.*;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
|
||||
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Author scott
|
||||
*/
|
||||
@Configuration
|
||||
@EnableSwagger2 //开启 Swagger2
|
||||
@EnableKnife4j //开启 knife4j,可以不写
|
||||
@Import(BeanValidatorPluginsConfiguration.class)
|
||||
public class Swagger2Config implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 解决springboot2.6 和springfox不兼容问题
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
|
||||
return new BeanPostProcessor() {
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
|
||||
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
|
||||
List<T> copy = mappings.stream()
|
||||
.filter(mapping -> mapping.getPatternParser() == null)
|
||||
.collect(Collectors.toList());
|
||||
mappings.clear();
|
||||
mappings.addAll(copy);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
|
||||
try {
|
||||
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
|
||||
field.setAccessible(true);
|
||||
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 显示swagger-ui.html文档展示页,还必须注入swagger资源:
|
||||
*
|
||||
* @param registry
|
||||
*/
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
|
||||
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
|
||||
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
|
||||
}
|
||||
|
||||
/**
|
||||
* swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等
|
||||
*
|
||||
* @return Docket
|
||||
*/
|
||||
|
||||
@Bean(value = "digital-application")
|
||||
public Docket zitaApplication() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo()).groupName("换装服务")
|
||||
.select()
|
||||
//此包路径下的类,才生成接口文档
|
||||
.apis(RequestHandlerSelectors.basePackage("digital.application"))
|
||||
//加了ApiOperation注解的类,才生成接口文档
|
||||
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
|
||||
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
|
||||
.paths(PathSelectors.any())
|
||||
.build()
|
||||
.securitySchemes(Collections.singletonList(securityScheme()))
|
||||
.securityContexts(securityContexts());
|
||||
//.globalOperationParameters(setHeaderToken());
|
||||
}
|
||||
|
||||
/***
|
||||
* oauth2配置
|
||||
* 需要增加swagger授权回调地址
|
||||
* http://localhost:8888/webjars/springfox-swagger-ui/o2c.html
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
SecurityScheme securityScheme() {
|
||||
return new ApiKey(CommonConstant.X_ACCESS_TOKEN, CommonConstant.X_ACCESS_TOKEN, "header");
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT token
|
||||
* @return
|
||||
*/
|
||||
private List<Parameter> setHeaderToken() {
|
||||
ParameterBuilder tokenPar = new ParameterBuilder();
|
||||
List<Parameter> pars = new ArrayList<>();
|
||||
tokenPar.name(CommonConstant.X_ACCESS_TOKEN).description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
|
||||
pars.add(tokenPar.build());
|
||||
return pars;
|
||||
}
|
||||
|
||||
/**
|
||||
* api文档的详细信息函数,注意这里的注解引用的是哪个
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
// //大标题
|
||||
.title("换装体验API接口文档")
|
||||
// 版本号
|
||||
.version("1.0")
|
||||
// .termsOfServiceUrl("NO terms of service")
|
||||
// 描述
|
||||
.description("后台API接口")
|
||||
// 作者
|
||||
.contact(new Contact("zita", "", ""))
|
||||
// .contact("JEECG团队")
|
||||
.license("The Apache License, Version 2.0")
|
||||
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增 securityContexts 保持登录状态
|
||||
*/
|
||||
private List<SecurityContext> securityContexts() {
|
||||
return new ArrayList(
|
||||
Collections.singleton(SecurityContext.builder()
|
||||
.securityReferences(defaultAuth())
|
||||
.forPaths(PathSelectors.regex("^(?!auth).*$"))
|
||||
.build())
|
||||
);
|
||||
}
|
||||
|
||||
private List<SecurityReference> defaultAuth() {
|
||||
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
|
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
|
||||
authorizationScopes[0] = authorizationScope;
|
||||
return new ArrayList(
|
||||
Collections.singleton(new SecurityReference(CommonConstant.X_ACCESS_TOKEN, authorizationScopes)));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package digital.config;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class ThreadPoolConfiguration {
|
||||
|
||||
@Bean(name = "defaultThreadPoolExecutor", destroyMethod = "shutdown")
|
||||
public ThreadPoolExecutor systemCheckPoolExecutorService() {
|
||||
|
||||
return new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<Runnable>(10000),
|
||||
new ThreadFactoryBuilder().setNameFormat("default-executor-%d").build(),
|
||||
(r, executor) -> log.error("system pool is full! "));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package digital.config;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||
import io.micrometer.prometheus.PrometheusMeterRegistry;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
//import org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Spring Boot 2.0 解决跨域问题
|
||||
*
|
||||
* @Author qinfeng
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Value("${jeecg.path.upload}")
|
||||
private String upLoadPath;
|
||||
@Value("${jeecg.path.webapp}")
|
||||
private String webAppPath;
|
||||
@Value("${spring.resource.static-locations}")
|
||||
private String staticLocations;
|
||||
|
||||
@Autowired(required = false)
|
||||
private PrometheusMeterRegistry prometheusMeterRegistry;
|
||||
|
||||
/**
|
||||
* 国际化配置
|
||||
*/
|
||||
@Bean
|
||||
public LocaleResolver localeResolver(){
|
||||
return new MyLocaleResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态资源的配置 - 使得可以从磁盘中读取 Html、图片、视频、音频等
|
||||
*/
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/**")
|
||||
//update-begin-author:taoyan date:20211116 for: jeecg.path.webapp配置无效 #3126
|
||||
.addResourceLocations("file:" + upLoadPath + "//")
|
||||
.addResourceLocations("file:" + webAppPath + "//")
|
||||
//update-end-author:taoyan date:20211116 for: jeecg.path.webapp配置无效 #3126
|
||||
.addResourceLocations(staticLocations.split(","));
|
||||
}
|
||||
|
||||
/**
|
||||
* 方案一: 默认访问根路径跳转 doc.html页面 (swagger文档页面)
|
||||
* 方案二: 访问根路径改成跳转 index.html页面 (简化部署方案: 可以把前端打包直接放到项目的 webapp,上面的配置)
|
||||
*/
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/").setViewName("doc.html");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Conditional(CorsFilterCondition.class)
|
||||
public CorsFilter corsFilter() {
|
||||
final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
|
||||
final CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
//是否允许请求带有验证信息
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
// 允许访问的客户端域名
|
||||
corsConfiguration.addAllowedOriginPattern("*");
|
||||
// 允许服务端访问的客户端请求头
|
||||
corsConfiguration.addAllowedHeader("*");
|
||||
// 允许访问的方法名,GET POST等
|
||||
corsConfiguration.addAllowedMethod("*");
|
||||
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return new CorsFilter(urlBasedCorsConfigurationSource);
|
||||
}
|
||||
@Override
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(objectMapper());
|
||||
converters.add(jackson2HttpMessageConverter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义ObjectMapper
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
public ObjectMapper objectMapper() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
//处理bigDecimal
|
||||
objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
|
||||
objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
|
||||
//处理失败
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, false);
|
||||
//默认的处理日期时间格式
|
||||
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
||||
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
||||
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
|
||||
objectMapper.registerModule(javaTimeModule);
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* SpringBootAdmin的Httptrace不见了
|
||||
* https://blog.csdn.net/u013810234/article/details/110097201
|
||||
*/
|
||||
// @Bean
|
||||
// public InMemoryHttpTraceRepository getInMemoryHttpTrace(){
|
||||
// return new InMemoryHttpTraceRepository();
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* 解决metrics端点不显示jvm信息的问题(zyf)
|
||||
*/
|
||||
@Bean
|
||||
InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor) {
|
||||
return () -> meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package digital.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
/**
|
||||
* @Description: WebSocketConfig
|
||||
* @author: smcp
|
||||
*/
|
||||
@Configuration
|
||||
public class WebSocketConfig {
|
||||
/**
|
||||
* 注入ServerEndpointExporter,
|
||||
* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
|
||||
*/
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter() {
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package digital.config.filter;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* author: smcp
|
||||
* date:2022-08-08 16:31
|
||||
* description:
|
||||
**/
|
||||
|
||||
@Configuration
|
||||
public class FilterConfig {
|
||||
// @Resource
|
||||
// private MyAesFilter myAesFilter;
|
||||
//
|
||||
// @Bean
|
||||
// public FilterRegistrationBean testFilterRegistration() {
|
||||
// FilterRegistrationBean registration = new FilterRegistrationBean(myAesFilter);
|
||||
// registration.addUrlPatterns("/*");
|
||||
// registration.setName("myAesFilter");
|
||||
// registration.setOrder(1);
|
||||
// return registration;
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package digital.config.filter;
|
||||
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import digital.util.encryption.AesEncryptUtil;
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* author: smcp
|
||||
* date:2022-08-08 16:23
|
||||
* description:
|
||||
**/
|
||||
//@Component
|
||||
@Slf4j
|
||||
public class MyAesFilter implements Filter {
|
||||
// @Value("${admin.encrypt.excludeUrl}")
|
||||
private String ignoreStr;
|
||||
private String[] ignoreArr;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
/**
|
||||
* 有错误相应返回-44
|
||||
*
|
||||
* @param response
|
||||
* @throws IOException
|
||||
*/
|
||||
private void getFailResponse(HttpServletResponse response) throws IOException {
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("application/json; charset=utf-8");
|
||||
PrintWriter out = null;
|
||||
out = response.getWriter();
|
||||
// out.write("{\n" +
|
||||
// " \"status\":"+ Constant.ENCRYPT_FAIL +",\n" +
|
||||
// " \"message\": null,\n" +
|
||||
// " \"data\": []\n" +
|
||||
// "}");
|
||||
//加密后的错误消息
|
||||
out.write("+D+JO8tuwkrNbxnTTLdqStifmQceT+LlYETnIG/JZKrbAn+gIiqIp3VbzBV1y6R8B7aY53VM2xHa7cY3Osbnqw==");
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
|
||||
// if (ignoreArr != null) {
|
||||
// ignoreArr = ignoreStr.split(",");
|
||||
// }
|
||||
// HttpServletRequest HttpRequest = (HttpServletRequest) request;
|
||||
// HttpServletResponse HttpResponse = (HttpServletResponse) response;
|
||||
// boolean flag = isIgnore(HttpRequest, ignoreArr);
|
||||
// if (flag) {
|
||||
// try {
|
||||
// chain.doFilter(HttpRequest, HttpResponse);
|
||||
// } catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
// } catch (ServletException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// } else {
|
||||
try {
|
||||
//响应处理 包装响应对象 res 并缓存响应数据
|
||||
ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response);
|
||||
//执行业务逻辑 交给下一个过滤器或servlet处理
|
||||
chain.doFilter(request, responseWrapper);
|
||||
byte[] resData = responseWrapper.getResponseData();
|
||||
//设置响应内容格式,防止解析响应内容时出错
|
||||
// responseWrapper.setContentType("text/plain;charset=UTF-8");
|
||||
//加密响应报文并响应
|
||||
String encryptBASE64 = AesEncryptUtil.encrypt(new String(resData));
|
||||
PrintWriter out = response.getWriter();
|
||||
out.print(encryptBASE64);
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
getFailResponse((HttpServletResponse) response);
|
||||
} catch (IOException ioException) {
|
||||
ioException.printStackTrace();
|
||||
}
|
||||
e.printStackTrace();
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
/**
|
||||
* 哪些路径不处理
|
||||
*
|
||||
* @param request
|
||||
* @param strArr
|
||||
* @return
|
||||
*/
|
||||
public boolean isIgnore(HttpServletRequest request, String[] strArr) {
|
||||
String path = request.getRequestURI();
|
||||
for (String ignore : strArr) {
|
||||
if (path.contains(ignore)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package digital.config.filter;
|
||||
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* author: smcp
|
||||
* date:2022-08-08 17:48
|
||||
* description:
|
||||
**/
|
||||
public class RequestWrapper extends HttpServletRequestWrapper {
|
||||
private String requestBody = null;
|
||||
//请求体
|
||||
private HttpServletRequest req = null;
|
||||
// private final byte[] body;//保存流的字节数组
|
||||
private final Map<String, String> reqHeaders=new HashMap<>();
|
||||
public RequestWrapper(HttpServletRequest request) throws IOException {
|
||||
super(request);
|
||||
this.req = request;
|
||||
// this.reqHeaders = new HashMap<String, String>();
|
||||
// String sessionStream = getRequestBodyStr(request);//读取流中的参数
|
||||
// body = sessionStream.getBytes(Charset.forName("UTF-8"));
|
||||
}
|
||||
public RequestWrapper(HttpServletRequest request, String requestBody) {
|
||||
super(request);
|
||||
this.requestBody = requestBody;
|
||||
this.req = request;
|
||||
// this.reqHeaders = request.get;
|
||||
}
|
||||
/**
|
||||
* @Description: 获取请求body
|
||||
* @Date: 2020/5/26 10:31
|
||||
* @Param: [request]
|
||||
* @Return: java.lang.String
|
||||
*/
|
||||
public String getRequestBodyStr(final ServletRequest request) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
InputStream inputStream = null;
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
inputStream = cloneInputStream(request.getInputStream());
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
|
||||
String line = "";
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
/**
|
||||
* @Description: 复制输入流
|
||||
* @Param: [inputStream]
|
||||
* @Return: java.io.InputStream
|
||||
*/
|
||||
public InputStream cloneInputStream(ServletInputStream inputStream) throws IOException {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = inputStream.read(buffer)) > -1) {
|
||||
byteArrayOutputStream.write(buffer, 0, len);
|
||||
}
|
||||
byteArrayOutputStream.flush();
|
||||
InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
||||
return byteArrayInputStream;
|
||||
}
|
||||
@Override
|
||||
public BufferedReader getReader() throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(getInputStream()));
|
||||
}
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody.getBytes(req.getCharacterEncoding()));
|
||||
return new ServletInputStream() {
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public void setReadListener(ReadListener readListener) {
|
||||
}
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return bais.read();
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 添加header的名称和值
|
||||
*
|
||||
* @param name
|
||||
* @param value
|
||||
*/
|
||||
public void addHeader(String name, String value) {
|
||||
reqHeaders.put(name, value);
|
||||
}
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
// log.info("getHeader --->{}", name);
|
||||
String headerValue = super.getHeader(name);
|
||||
if (reqHeaders.containsKey(name)) {
|
||||
headerValue = reqHeaders.get(name);
|
||||
}
|
||||
return headerValue;
|
||||
}
|
||||
/**
|
||||
* 得到headers的名称
|
||||
*/
|
||||
@Override
|
||||
public Enumeration<String> getHeaderNames() {
|
||||
List<String> names = Collections.list(super.getHeaderNames());
|
||||
for (String name : reqHeaders.keySet()) {
|
||||
names.add(name);
|
||||
}
|
||||
return Collections.enumeration(names);
|
||||
}
|
||||
@Override
|
||||
public Enumeration<String> getHeaders(String name) {
|
||||
// log.info("getHeaders name --->>>>>>{}", name);
|
||||
List<String> values = Collections.list(super.getHeaders(name));
|
||||
// log.info("getHeaders value --->>>>>>{}", values);
|
||||
if (reqHeaders.containsKey(name)) {
|
||||
values = Arrays.asList(reqHeaders.get(name));
|
||||
}
|
||||
return Collections.enumeration(values);
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package digital.config.filter;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* author: smcp
|
||||
* date:2022-08-08 17:49
|
||||
* description:
|
||||
**/
|
||||
public class ResponseWrapper extends HttpServletResponseWrapper {
|
||||
private ByteArrayOutputStream buffer = null;
|
||||
private ServletOutputStream out = null;
|
||||
private PrintWriter writer = null;
|
||||
public ResponseWrapper(HttpServletResponse response) throws IOException {
|
||||
super(response);
|
||||
buffer = new ByteArrayOutputStream();// 真正存储数据的流
|
||||
out = new WapperedOutputStream(buffer);
|
||||
writer = new PrintWriter(new OutputStreamWriter(buffer,this.getCharacterEncoding()));
|
||||
}
|
||||
/** 重载父类获取outputstream的方法 */
|
||||
@Override
|
||||
public ServletOutputStream getOutputStream() throws IOException {
|
||||
return out;
|
||||
}
|
||||
/** 重载父类获取writer的方法 */
|
||||
@Override
|
||||
public PrintWriter getWriter() throws UnsupportedEncodingException {
|
||||
return writer;
|
||||
}
|
||||
/** 重载父类获取flushBuffer的方法 */
|
||||
@Override
|
||||
public void flushBuffer() throws IOException {
|
||||
if (out != null) {
|
||||
out.flush();
|
||||
}
|
||||
if (writer != null) {
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void reset() {
|
||||
buffer.reset();
|
||||
}
|
||||
/** 将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据 */
|
||||
public byte[] getResponseData() throws IOException {
|
||||
flushBuffer();
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
/** 内部类,对ServletOutputStream进行包装 */
|
||||
private class WapperedOutputStream extends ServletOutputStream {
|
||||
private ByteArrayOutputStream bos = null;
|
||||
public WapperedOutputStream(ByteArrayOutputStream stream)
|
||||
throws IOException {
|
||||
bos = stream;
|
||||
}
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
bos.write(b);
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
bos.write(b, 0, b.length);
|
||||
}
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public void setWriteListener(WriteListener writeListener) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//package digital.config.mns;
|
||||
//
|
||||
//import org.springframework.beans.factory.annotation.Value;
|
||||
//import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//
|
||||
//@Configuration
|
||||
//@ConfigurationProperties(prefix = "mns")
|
||||
//public class MnsConfiguration {
|
||||
//
|
||||
// @Value("${mns.accountEndpoint}")
|
||||
// private String accountEndpoint;
|
||||
// @Value("${mns.accessKeyId}")
|
||||
// private String accessKeyId;
|
||||
//
|
||||
// @Value("${mns.accessKeySecret}")
|
||||
// private String accessKeySecret;
|
||||
//
|
||||
// public String getAccountEndpoint() {
|
||||
// return accountEndpoint;
|
||||
// }
|
||||
//
|
||||
// public void setAccountEndpoint(String accountEndpoint) {
|
||||
// this.accountEndpoint = accountEndpoint;
|
||||
// }
|
||||
//
|
||||
// public String getAccessKeyId() {
|
||||
// return accessKeyId;
|
||||
// }
|
||||
//
|
||||
// public void setAccessKeyId(String accessKeyId) {
|
||||
// this.accessKeyId = accessKeyId;
|
||||
// }
|
||||
//
|
||||
// public String getAccessKeySecret() {
|
||||
// return accessKeySecret;
|
||||
// }
|
||||
//
|
||||
// public void setAccessKeySecret(String accessKeySecret) {
|
||||
// this.accessKeySecret = accessKeySecret;
|
||||
// }
|
||||
//}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
//package digital.config.mqtoken;
|
||||
//
|
||||
//import javax.servlet.*;
|
||||
//import javax.servlet.http.HttpServletRequest;
|
||||
//import java.io.IOException;
|
||||
//
|
||||
///**
|
||||
// * 存放临时令牌Token到线程上下文
|
||||
// *
|
||||
// * 供队列、定时任务 feign调用使用(解决无会话Token问题)
|
||||
// * @author zyf
|
||||
// */
|
||||
//public class TransmitUserTokenFilter implements Filter {
|
||||
//
|
||||
// private static String X_ACCESS_TOKEN="X-Access-Token";
|
||||
//
|
||||
// public TransmitUserTokenFilter() {
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void init(FilterConfig filterConfig) throws ServletException {
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
// this.initUserInfo((HttpServletRequest) request);
|
||||
// chain.doFilter(request, response);
|
||||
// }
|
||||
//
|
||||
// private void initUserInfo(HttpServletRequest request) {
|
||||
// String token = request.getHeader(X_ACCESS_TOKEN);
|
||||
// if (token!=null) {
|
||||
// try {
|
||||
// //将Token放入当前线程上下文中
|
||||
// UserTokenContext.setToken(token);
|
||||
// } catch (Exception e) {
|
||||
// //e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void destroy() {
|
||||
// }
|
||||
//}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package digital.config.mqtoken;
|
||||
|
||||
|
||||
/**
|
||||
* 用户Token线程上下文
|
||||
*
|
||||
* 供队列、定时任务 feign调用使用(解决无会话Token问题)
|
||||
* @author zyf
|
||||
*/
|
||||
public class UserTokenContext {
|
||||
|
||||
/**
|
||||
* 当前线程的TOKEN副本
|
||||
*/
|
||||
private static ThreadLocal<String> userToken = new ThreadLocal<String>();
|
||||
|
||||
public UserTokenContext() {
|
||||
}
|
||||
|
||||
public static String getToken(){
|
||||
return userToken.get();
|
||||
}
|
||||
|
||||
public static void setToken(String token){
|
||||
userToken.set(token);
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
userToken.remove();
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
//package digital.config.mybatis;
|
||||
//
|
||||
//import net.sf.jsqlparser.expression.BinaryExpression;
|
||||
//import net.sf.jsqlparser.expression.Expression;
|
||||
//import net.sf.jsqlparser.expression.Parenthesis;
|
||||
//import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||
//import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||
//import net.sf.jsqlparser.expression.operators.relational.*;
|
||||
//import net.sf.jsqlparser.schema.Column;
|
||||
//import net.sf.jsqlparser.schema.Table;
|
||||
//import net.sf.jsqlparser.statement.select.*;
|
||||
//
|
||||
//import java.util.List;
|
||||
//
|
||||
///**
|
||||
// * 复写租户条件
|
||||
// */
|
||||
//public class JeecgTenantParser extends TenantSqlParser {
|
||||
//
|
||||
// /**
|
||||
// * @param expression
|
||||
// * @param table
|
||||
// * @return
|
||||
// */
|
||||
// protected Expression processTableAlias(Expression expression, Table table) {
|
||||
// String tableAliasName;
|
||||
// if (table.getAlias() == null) {
|
||||
// tableAliasName = table.getName();
|
||||
// } else {
|
||||
// tableAliasName = table.getAlias().getName();
|
||||
// }
|
||||
//
|
||||
// // in
|
||||
// if (expression instanceof InExpression) {
|
||||
// InExpression in = (InExpression) expression;
|
||||
// if (in.getLeftExpression() instanceof Column) {
|
||||
// setTableAliasNameForColumn((Column) in.getLeftExpression(), tableAliasName);
|
||||
// }
|
||||
//
|
||||
// // 比较操作
|
||||
// } else if (expression instanceof BinaryExpression) {
|
||||
// BinaryExpression compare = (BinaryExpression) expression;
|
||||
// if (compare.getLeftExpression() instanceof Column) {
|
||||
// setTableAliasNameForColumn((Column) compare.getLeftExpression(), tableAliasName);
|
||||
// } else if (compare.getRightExpression() instanceof Column) {
|
||||
// setTableAliasNameForColumn((Column) compare.getRightExpression(), tableAliasName);
|
||||
// }
|
||||
//
|
||||
// // between
|
||||
// } else if (expression instanceof Between) {
|
||||
// Between between = (Between) expression;
|
||||
// if (between.getLeftExpression() instanceof Column) {
|
||||
// setTableAliasNameForColumn((Column) between.getLeftExpression(), tableAliasName);
|
||||
// }
|
||||
// }
|
||||
// return expression;
|
||||
// }
|
||||
//
|
||||
// private void setTableAliasNameForColumn(Column column, String tableAliasName) {
|
||||
// column.setColumnName(tableAliasName + "." + column.getColumnName());
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 默认是按 tenant_id=1 按等于条件追加
|
||||
// *
|
||||
// * @param currentExpression 现有的条件:比如你原来的sql查询条件
|
||||
// * @param table
|
||||
// * @return
|
||||
// */
|
||||
// @Override
|
||||
// protected Expression builderExpression(Expression currentExpression, Table table) {
|
||||
// final Expression tenantExpression = this.getTenantHandler().getTenantId(true);
|
||||
// Expression appendExpression;
|
||||
// if (!(tenantExpression instanceof SupportsOldOracleJoinSyntax)) {
|
||||
// appendExpression = new EqualsTo();
|
||||
// ((EqualsTo) appendExpression).setLeftExpression(this.getAliasColumn(table));
|
||||
// ((EqualsTo) appendExpression).setRightExpression(tenantExpression);
|
||||
// } else {
|
||||
// appendExpression = processTableAlias(tenantExpression, table);
|
||||
// }
|
||||
// if (currentExpression == null) {
|
||||
// return appendExpression;
|
||||
// }
|
||||
// if (currentExpression instanceof BinaryExpression) {
|
||||
// BinaryExpression binaryExpression = (BinaryExpression) currentExpression;
|
||||
// if (binaryExpression.getLeftExpression() instanceof FromItem) {
|
||||
// processFromItem((FromItem) binaryExpression.getLeftExpression());
|
||||
// }
|
||||
// if (binaryExpression.getRightExpression() instanceof FromItem) {
|
||||
// processFromItem((FromItem) binaryExpression.getRightExpression());
|
||||
// }
|
||||
// } else if (currentExpression instanceof InExpression) {
|
||||
// InExpression inExp = (InExpression) currentExpression;
|
||||
// ItemsList rightItems = inExp.getRightItemsList();
|
||||
// if (rightItems instanceof SubSelect) {
|
||||
// processSelectBody(((SubSelect) rightItems).getSelectBody());
|
||||
// }
|
||||
// }
|
||||
// if (currentExpression instanceof OrExpression) {
|
||||
// return new AndExpression(new Parenthesis(currentExpression), appendExpression);
|
||||
// } else {
|
||||
// return new AndExpression(currentExpression, appendExpression);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void processPlainSelect(PlainSelect plainSelect, boolean addColumn) {
|
||||
// FromItem fromItem = plainSelect.getFromItem();
|
||||
// if (fromItem instanceof Table) {
|
||||
// Table fromTable = (Table) fromItem;
|
||||
// if (!this.getTenantHandler().doTableFilter(fromTable.getName())) {
|
||||
// plainSelect.setWhere(builderExpression(plainSelect.getWhere(), fromTable));
|
||||
// if (addColumn) {
|
||||
// plainSelect.getSelectItems().add(new SelectExpressionItem(new Column(this.getTenantHandler().getTenantIdColumn())));
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// processFromItem(fromItem);
|
||||
// }
|
||||
// List<Join> joins = plainSelect.getJoins();
|
||||
// if (joins != null && joins.size() > 0) {
|
||||
// joins.forEach(j -> {
|
||||
// processJoin(j);
|
||||
// processFromItem(j.getRightItem());
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
package digital.config.mybatis;
|
||||
|
||||
import digital.base.vo.LoginUser;
|
||||
import digital.util.util.SpringContextUtils;
|
||||
import digital.util.util.oConvertUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.binding.MapperMethod.ParamMap;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* mybatis拦截器,自动注入创建人、创建时间、修改人、修改时间
|
||||
* @Author scott
|
||||
* @Date 2019-01-19
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
|
||||
public class MybatisInterceptor implements Interceptor {
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
|
||||
String sqlId = mappedStatement.getId();
|
||||
log.debug("------sqlId------" + sqlId);
|
||||
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
|
||||
Object parameter = invocation.getArgs()[1];
|
||||
log.debug("------sqlCommandType------" + sqlCommandType);
|
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||
if (parameter == null) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
if (SqlCommandType.INSERT == sqlCommandType) {
|
||||
LoginUser sysUser = this.getLoginUser();
|
||||
Field[] fields = oConvertUtils.getAllFields(parameter);
|
||||
for (Field field : fields) {
|
||||
log.debug("------field.name------" + field.getName());
|
||||
try {
|
||||
if ("createBy".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
Object localCreateBy = field.get(parameter);
|
||||
field.setAccessible(false);
|
||||
if (localCreateBy == null || "".equals(localCreateBy)) {
|
||||
if (sysUser != null) {
|
||||
// 登录人账号
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, sysUser.getUsername());
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 注入创建时间
|
||||
if ("createTime".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
Object localCreateDate = field.get(parameter);
|
||||
field.setAccessible(false);
|
||||
if (localCreateDate == null || "".equals(localCreateDate)) {
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, new Date());
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
//注入部门编码
|
||||
if ("sysOrgCode".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
Object localSysOrgCode = field.get(parameter);
|
||||
field.setAccessible(false);
|
||||
if (localSysOrgCode == null || "".equals(localSysOrgCode)) {
|
||||
// 获取登录用户信息
|
||||
if (sysUser != null) {
|
||||
field.setAccessible(true);
|
||||
// field.set(parameter, sysUser.getOrgCode());
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 注入appKet
|
||||
if ("appKey".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
Object appKey = field.get(parameter);
|
||||
field.setAccessible(false);
|
||||
if (appKey == null || "".equals(appKey)) {
|
||||
String headAppKey = request.getHeader("appKey");
|
||||
if (oConvertUtils.isNotEmpty(headAppKey)){
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, headAppKey);
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (SqlCommandType.UPDATE == sqlCommandType) {
|
||||
LoginUser sysUser = this.getLoginUser();
|
||||
Field[] fields = null;
|
||||
if (parameter instanceof ParamMap) {
|
||||
ParamMap<?> p = (ParamMap<?>) parameter;
|
||||
//update-begin-author:scott date:20190729 for:批量更新报错issues/IZA3Q--
|
||||
String et = "et";
|
||||
if (p.containsKey(et)) {
|
||||
parameter = p.get(et);
|
||||
} else {
|
||||
parameter = p.get("param1");
|
||||
}
|
||||
//update-end-author:scott date:20190729 for:批量更新报错issues/IZA3Q-
|
||||
|
||||
//update-begin-author:scott date:20190729 for:更新指定字段时报错 issues/#516-
|
||||
if (parameter == null) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
//update-end-author:scott date:20190729 for:更新指定字段时报错 issues/#516-
|
||||
|
||||
fields = oConvertUtils.getAllFields(parameter);
|
||||
} else {
|
||||
fields = oConvertUtils.getAllFields(parameter);
|
||||
}
|
||||
|
||||
for (Field field : fields) {
|
||||
log.debug("------field.name------" + field.getName());
|
||||
try {
|
||||
if ("updateBy".equals(field.getName())) {
|
||||
//获取登录用户信息
|
||||
if (sysUser != null) {
|
||||
// 登录账号
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, sysUser.getUsername());
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
if ("updateTime".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, new Date());
|
||||
field.setAccessible(false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object plugin(Object target) {
|
||||
return Plugin.wrap(target, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperties(Properties properties) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
//update-begin--Author:scott Date:20191213 for:关于使用Quzrtz 开启线程任务, #465
|
||||
/**
|
||||
* 获取登录用户
|
||||
* @return
|
||||
*/
|
||||
private LoginUser getLoginUser() {
|
||||
LoginUser sysUser = null;
|
||||
try {
|
||||
sysUser = SecurityUtils.getSubject().getPrincipal() != null ? (LoginUser) SecurityUtils.getSubject().getPrincipal() : null;
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
sysUser = null;
|
||||
}
|
||||
return sysUser;
|
||||
}
|
||||
//update-end--Author:scott Date:20191213 for:关于使用Quzrtz 开启线程任务, #465
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package digital.config.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import digital.base.constant.CommonConstant;
|
||||
import digital.util.util.SpringContextUtils;
|
||||
import digital.util.util.oConvertUtils;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.StringValue;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 单数据源配置(jeecg.datasource.open = false时生效)
|
||||
*
|
||||
* @Author zhoujf
|
||||
*/
|
||||
@Configuration
|
||||
//@MapperScan(value = {"org.jeecg.modules.**.mapper*", "smcp.**.mapper*"})
|
||||
@MapperScan(value = {"digital.**.mapper*"})
|
||||
public class MybatisPlusSaasConfig {
|
||||
/**
|
||||
* tenant_id 字段名
|
||||
*/
|
||||
private static final String TENANT_FIELD_NAME = "app_key";
|
||||
/**
|
||||
* 哪些表需要做多租户 表需要添加一个字段 tenant_id
|
||||
*/
|
||||
private static final List<String> TENANT_TABLE = new ArrayList<String>();
|
||||
|
||||
static {
|
||||
// TENANT_TABLE.add("audio_manage");
|
||||
// TENANT_TABLE.add("background_manage");
|
||||
// TENANT_TABLE.add("digital_manage");
|
||||
// TENANT_TABLE.add("sys_file");
|
||||
// TENANT_TABLE.add("digital_manage");
|
||||
// TENANT_TABLE.add("video_manage");
|
||||
// //角色、菜单、部门
|
||||
// tenantTable.add("sys_role");
|
||||
// tenantTable.add("sys_permission");
|
||||
// tenantTable.add("sys_depart");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
|
||||
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
|
||||
@Override
|
||||
public Expression getTenantId() {
|
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||
if (Objects.isNull(request)) {
|
||||
return new StringValue("");
|
||||
}
|
||||
String tenantId = oConvertUtils.getString(request.getHeader("appKey"), "0");
|
||||
return new StringValue(tenantId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTenantIdColumn() {
|
||||
return TENANT_FIELD_NAME;
|
||||
}
|
||||
|
||||
// 返回 true 表示不走租户逻辑
|
||||
@Override
|
||||
public boolean ignoreTable(String tableName) {
|
||||
HttpServletRequest req = SpringContextUtils.getHttpServletRequest();
|
||||
// String tenantId = req.getHeader(CommonConstant.TENANT_ID);
|
||||
String tenantId = oConvertUtils.getString(TenantContext.getTenant(), "0");
|
||||
//判断是否是超管
|
||||
if (CommonConstant.ADMIN_TENANT_ID.equals(tenantId)) {
|
||||
return true;
|
||||
}
|
||||
for (String temp : TENANT_TABLE) {
|
||||
if (temp.equalsIgnoreCase(tableName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 下个版本会删除,现在为了避免缓存出现问题不得不配置
|
||||
// * @return
|
||||
// */
|
||||
// @Bean
|
||||
// public ConfigurationCustomizer configurationCustomizer() {
|
||||
// return configuration -> configuration.setUseDeprecatedExecutor(false);
|
||||
// }
|
||||
// /**
|
||||
// * mybatis-plus SQL执行效率插件【生产环境可以关闭】
|
||||
// */
|
||||
// @Bean
|
||||
// public PerformanceInterceptor performanceInterceptor() {
|
||||
// return new PerformanceInterceptor();
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package digital.config.mybatis;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 多租户 tenant_id存储器
|
||||
* @author: smcp
|
||||
*/
|
||||
@Slf4j
|
||||
public class TenantContext {
|
||||
private static ThreadLocal<String> currentTenant = new ThreadLocal<>();
|
||||
|
||||
public static void setTenant(String tenant) {
|
||||
log.debug(" setting tenant to " + tenant);
|
||||
currentTenant.set(tenant);
|
||||
}
|
||||
|
||||
public static String getTenant() {
|
||||
return currentTenant.get();
|
||||
}
|
||||
|
||||
public static void clear(){
|
||||
currentTenant.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//package digital.config.oss;
|
||||
//
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.springframework.beans.factory.annotation.Value;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//import digital.base.constant.CommonConstant;
|
||||
//import digital.base.constant.SymbolConstant;
|
||||
//
|
||||
///**
|
||||
// * Minio文件上传配置文件
|
||||
// * @author: smcp
|
||||
// */
|
||||
//@Slf4j
|
||||
//@Configuration
|
||||
//public class MinioConfig {
|
||||
// @Value(value = "${jeecg.minio.minio_url}")
|
||||
// private String minioUrl;
|
||||
// @Value(value = "${jeecg.minio.minio_name}")
|
||||
// private String minioName;
|
||||
// @Value(value = "${jeecg.minio.minio_pass}")
|
||||
// private String minioPass;
|
||||
// @Value(value = "${jeecg.minio.bucketName}")
|
||||
// private String bucketName;
|
||||
//
|
||||
// @Bean
|
||||
// public void initMinio() {
|
||||
// if (!minioUrl.startsWith(CommonConstant.STR_HTTP)) {
|
||||
// minioUrl = "http://" + minioUrl;
|
||||
// }
|
||||
// if (!minioUrl.endsWith(SymbolConstant.SINGLE_SLASH)) {
|
||||
// minioUrl = minioUrl.concat(SymbolConstant.SINGLE_SLASH);
|
||||
// }
|
||||
//// MinioUtil.setMinioUrl(minioUrl);
|
||||
//// MinioUtil.setMinioName(minioName);
|
||||
//// MinioUtil.setMinioPass(minioPass);
|
||||
//// MinioUtil.setBucketName(bucketName);
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@@ -0,0 +1,39 @@
|
||||
package digital.config.oss;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import digital.util.oss.OssBootUtil;
|
||||
|
||||
/**
|
||||
* 云存储 配置
|
||||
*
|
||||
* @author: smcp
|
||||
*/
|
||||
@Configuration
|
||||
@Data
|
||||
public class OssConfiguration {
|
||||
@Value("${jeecg.oss.endpoint}")
|
||||
private String endpoint;
|
||||
@Value("${jeecg.oss.accessKey}")
|
||||
private String accessKeyId;
|
||||
@Value("${jeecg.oss.secretKey}")
|
||||
private String accessKeySecret;
|
||||
@Value("${jeecg.oss.bucketName}")
|
||||
private String bucketName;
|
||||
@Value("${jeecg.oss.staticDomain}")
|
||||
private String staticDomain;
|
||||
// @Value("${jeecg.oss.callBackUrl}")
|
||||
// private String callBackUrl;
|
||||
|
||||
@Bean
|
||||
public void initOssBootConfiguration() {
|
||||
OssBootUtil.setEndPoint(endpoint);
|
||||
OssBootUtil.setAccessKeyId(accessKeyId);
|
||||
OssBootUtil.setAccessKeySecret(accessKeySecret);
|
||||
OssBootUtil.setBucketName(bucketName);
|
||||
OssBootUtil.setStaticDomain(staticDomain);
|
||||
// OssBootUtil.setCallBackUrl(callBackUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package digital.config.shiro;
|
||||
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
|
||||
/**
|
||||
* @Author Scott
|
||||
* @create 2018-07-12 15:19
|
||||
* @desc
|
||||
**/
|
||||
public class JwtToken implements AuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String token;
|
||||
|
||||
public JwtToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package digital.config.shiro.filters;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
||||
import org.apache.shiro.web.filter.InvalidRequestFilter;
|
||||
import org.apache.shiro.web.filter.mgt.DefaultFilter;
|
||||
import org.apache.shiro.web.filter.mgt.FilterChainManager;
|
||||
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
|
||||
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
|
||||
import org.apache.shiro.web.mgt.WebSecurityManager;
|
||||
import org.apache.shiro.web.servlet.AbstractShiroFilter;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 自定义ShiroFilterFactoryBean解决资源中文路径问题
|
||||
* @author: smcp
|
||||
*/
|
||||
@Slf4j
|
||||
public class CustomShiroFilterFactoryBean extends ShiroFilterFactoryBean {
|
||||
@Override
|
||||
public Class getObjectType() {
|
||||
return MySpringShiroFilter.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractShiroFilter createInstance() throws Exception {
|
||||
|
||||
SecurityManager securityManager = getSecurityManager();
|
||||
if (securityManager == null) {
|
||||
String msg = "SecurityManager property must be set.";
|
||||
throw new BeanInitializationException(msg);
|
||||
}
|
||||
|
||||
if (!(securityManager instanceof WebSecurityManager)) {
|
||||
String msg = "The security manager does not implement the WebSecurityManager interface.";
|
||||
throw new BeanInitializationException(msg);
|
||||
}
|
||||
|
||||
FilterChainManager manager = createFilterChainManager();
|
||||
//Expose the constructed FilterChainManager by first wrapping it in a
|
||||
// FilterChainResolver implementation. The AbstractShiroFilter implementations
|
||||
// do not know about FilterChainManagers - only resolvers:
|
||||
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
|
||||
chainResolver.setFilterChainManager(manager);
|
||||
|
||||
Map<String, Filter> filterMap = manager.getFilters();
|
||||
Filter invalidRequestFilter = filterMap.get(DefaultFilter.invalidRequest.name());
|
||||
if (invalidRequestFilter instanceof InvalidRequestFilter) {
|
||||
//此处是关键,设置false跳过URL携带中文400,servletPath中文校验bug
|
||||
((InvalidRequestFilter) invalidRequestFilter).setBlockNonAscii(false);
|
||||
}
|
||||
//Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
|
||||
//FilterChainResolver. It doesn't matter that the instance is an anonymous inner class
|
||||
//here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts
|
||||
//injection of the SecurityManager and FilterChainResolver:
|
||||
return new MySpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
|
||||
}
|
||||
|
||||
private static final class MySpringShiroFilter extends AbstractShiroFilter {
|
||||
protected MySpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
|
||||
if (webSecurityManager == null) {
|
||||
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
|
||||
} else {
|
||||
this.setSecurityManager(webSecurityManager);
|
||||
if (resolver != null) {
|
||||
this.setFilterChainResolver(resolver);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package digital.config.shiro.filters;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import digital.base.constant.CommonConstant;
|
||||
import digital.config.mybatis.TenantContext;
|
||||
import digital.config.shiro.JwtToken;
|
||||
import digital.util.util.JwtUtil;
|
||||
import digital.util.util.oConvertUtils;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @Description: 鉴权登录拦截器
|
||||
* @Author: Scott
|
||||
* @Date: 2018/10/7
|
||||
**/
|
||||
@Slf4j
|
||||
public class JwtFilter extends BasicHttpAuthenticationFilter {
|
||||
|
||||
/**
|
||||
* 默认开启跨域设置(使用单体)
|
||||
* 微服务情况下,此属性设置为false
|
||||
*/
|
||||
private boolean allowOrigin = true;
|
||||
|
||||
public JwtFilter(){}
|
||||
public JwtFilter(boolean allowOrigin){
|
||||
this.allowOrigin = allowOrigin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行登录认证
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param mappedValue
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
|
||||
try {
|
||||
executeLogin(request, response);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
JwtUtil.responseError(response, 401, CommonConstant.TOKEN_IS_INVALID_MSG);
|
||||
return false;
|
||||
//throw new AuthenticationException("Token失效,请重新登录", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
String token = httpServletRequest.getHeader(CommonConstant.X_ACCESS_TOKEN);
|
||||
// update-begin--Author:lvdandan Date:20210105 for:JT-355 OA聊天添加token验证,获取token参数
|
||||
if (oConvertUtils.isEmpty(token)) {
|
||||
token = httpServletRequest.getParameter("token");
|
||||
}
|
||||
// update-end--Author:lvdandan Date:20210105 for:JT-355 OA聊天添加token验证,获取token参数
|
||||
|
||||
JwtToken jwtToken = new JwtToken(token);
|
||||
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
|
||||
getSubject(request, response).login(jwtToken);
|
||||
// 如果没有抛出异常则代表登入成功,返回true
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对跨域提供支持
|
||||
*/
|
||||
@Override
|
||||
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
if(allowOrigin){
|
||||
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, httpServletRequest.getHeader(HttpHeaders.ORIGIN));
|
||||
// 允许客户端请求方法
|
||||
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET,POST,OPTIONS,PUT,DELETE");
|
||||
// 允许客户端提交的Header
|
||||
String requestHeaders = httpServletRequest.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
if (StringUtils.isNotEmpty(requestHeaders)) {
|
||||
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders);
|
||||
}
|
||||
// 允许客户端携带凭证信息(是否允许发送Cookie)
|
||||
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
|
||||
}
|
||||
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
|
||||
if (RequestMethod.OPTIONS.name().equalsIgnoreCase(httpServletRequest.getMethod())) {
|
||||
httpServletResponse.setStatus(HttpStatus.OK.value());
|
||||
return false;
|
||||
}
|
||||
//update-begin-author:taoyan date:20200708 for:多租户用到
|
||||
String tenantId = httpServletRequest.getHeader(CommonConstant.TENANT_ID);
|
||||
TenantContext.setTenant(tenantId);
|
||||
//update-end-author:taoyan date:20200708 for:多租户用到
|
||||
|
||||
return super.preHandle(request, response);
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package digital.config.shiro.filters;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.web.filter.AccessControlFilter;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @Author Scott
|
||||
* @create 2019-02-01 15:56
|
||||
* @desc 鉴权请求URL访问权限拦截器
|
||||
*/
|
||||
@Slf4j
|
||||
public class ResourceCheckFilter extends AccessControlFilter {
|
||||
|
||||
private String errorUrl;
|
||||
|
||||
public String getErrorUrl() {
|
||||
return errorUrl;
|
||||
}
|
||||
|
||||
public void setErrorUrl(String errorUrl) {
|
||||
this.errorUrl = errorUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示是否允许访问 ,如果允许访问返回true,否则false;
|
||||
*
|
||||
* @param servletRequest
|
||||
* @param servletResponse
|
||||
* @param o 表示写在拦截器中括号里面的字符串 mappedValue 就是 [urls] 配置中拦截器参数部分
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
|
||||
Subject subject = getSubject(servletRequest, servletResponse);
|
||||
String url = getPathWithinApplication(servletRequest);
|
||||
log.info("当前用户正在访问的 url => " + url);
|
||||
return subject.isPermitted(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* onAccessDenied:表示当访问拒绝时是否已经处理了; 如果返回 true 表示需要继续处理; 如果返回 false
|
||||
* 表示该拦截器实例已经处理了,将直接返回即可。
|
||||
*
|
||||
* @param servletRequest
|
||||
* @param servletResponse
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
|
||||
log.info("当 isAccessAllowed 返回 false 的时候,才会执行 method onAccessDenied ");
|
||||
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
response.sendRedirect(request.getContextPath() + this.errorUrl);
|
||||
|
||||
// 返回 false 表示已经处理,例如页面跳转啥的,表示不在走以下的拦截器了(如果还有配置的话)
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package digital.config.sign.interceptor;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* 签名 拦截器配置
|
||||
* @author: smcp
|
||||
*/
|
||||
@Configuration
|
||||
public class SignAuthConfiguration implements WebMvcConfigurer {
|
||||
public static String[] SIGN_URL_LIST = new String[]{"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*",
|
||||
"/sys/dict/loadDictOrderByValue/*", "/sys/dict/loadDictItem/*", "/sys/dict/loadTreeData",
|
||||
"/sys/api/queryTableDictItemsByCode", "/sys/api/queryFilterTableDictInfo", "/sys/api/queryTableDictByKeys",
|
||||
"/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"};
|
||||
@Bean
|
||||
public SignAuthInterceptor signAuthInterceptor() {
|
||||
return new SignAuthInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(signAuthInterceptor()).addPathPatterns(SIGN_URL_LIST);
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package digital.config.sign.interceptor;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import digital.base.constant.CommonConstant;
|
||||
import digital.base.vo.Result;
|
||||
import digital.config.sign.util.BodyReaderHttpServletRequestWrapper;
|
||||
import digital.config.sign.util.HttpUtils;
|
||||
import digital.config.sign.util.SignUtil;
|
||||
import digital.util.util.DateUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.SortedMap;
|
||||
|
||||
/**
|
||||
* 签名拦截器
|
||||
* @author qinfeng
|
||||
*/
|
||||
@Slf4j
|
||||
public class SignAuthInterceptor implements HandlerInterceptor {
|
||||
/**
|
||||
* 5分钟有效期
|
||||
*/
|
||||
private final static long MAX_EXPIRE = 5 * 60;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
log.info("request URI = " + request.getRequestURI());
|
||||
HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
|
||||
//获取全部参数(包括URL和body上的)
|
||||
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
|
||||
//对参数进行签名验证
|
||||
String headerSign = request.getHeader(CommonConstant.X_SIGN);
|
||||
String xTimestamp = request.getHeader(CommonConstant.X_TIMESTAMP);
|
||||
//客户端时间
|
||||
Long clientTimestamp = Long.parseLong(xTimestamp);
|
||||
|
||||
int length = 14;
|
||||
int length1000 = 1000;
|
||||
//1.校验签名时间(兼容X_TIMESTAMP的新老格式)
|
||||
if (xTimestamp.length() == length) {
|
||||
//a. X_TIMESTAMP格式是 yyyyMMddHHmmss (例子:20220308152143)
|
||||
if ((DateUtils.getCurrentTimestamp() - clientTimestamp) > MAX_EXPIRE) {
|
||||
log.error("签名验证失败:X-TIMESTAMP已过期,注意系统时间和服务器时间是否有误差!");
|
||||
throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP已过期");
|
||||
}
|
||||
} else {
|
||||
//b. X_TIMESTAMP格式是 时间戳 (例子:1646552406000)
|
||||
if ((System.currentTimeMillis() - clientTimestamp) > (MAX_EXPIRE * length1000)) {
|
||||
log.error("签名验证失败:X-TIMESTAMP已过期,注意系统时间和服务器时间是否有误差!");
|
||||
throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP已过期");
|
||||
}
|
||||
}
|
||||
|
||||
//2.校验签名
|
||||
boolean isSigned = SignUtil.verifySign(allParams,headerSign);
|
||||
|
||||
if (isSigned) {
|
||||
log.debug("Sign 签名通过!Header Sign : {}",headerSign);
|
||||
return true;
|
||||
} else {
|
||||
log.error("request URI = " + request.getRequestURI());
|
||||
log.error("Sign 签名校验失败!Header Sign : {}",headerSign);
|
||||
//校验失败返回前端
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("application/json; charset=utf-8");
|
||||
PrintWriter out = response.getWriter();
|
||||
Result<?> result = Result.error("Sign签名校验失败!");
|
||||
out.print(JSON.toJSON(result));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package digital.config.sign.util;
|
||||
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 保存过滤器里面的流
|
||||
*
|
||||
* @author jeecg
|
||||
* @date 20210621
|
||||
*/
|
||||
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private final byte[] body;
|
||||
|
||||
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) {
|
||||
|
||||
super(request);
|
||||
String sessionStream = getBodyString(request);
|
||||
body = sessionStream.getBytes(Charset.forName("UTF-8"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求Body
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public String getBodyString(final ServletRequest request) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try (InputStream inputStream = cloneInputStream(request.getInputStream());
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Description: 复制输入流</br>
|
||||
*
|
||||
* @param inputStream
|
||||
* @return</br>
|
||||
*/
|
||||
public InputStream cloneInputStream(ServletInputStream inputStream) {
|
||||
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
try {
|
||||
while ((len = inputStream.read(buffer)) > -1) {
|
||||
byteArrayOutputStream.write(buffer, 0, len);
|
||||
}
|
||||
byteArrayOutputStream.flush();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader() {
|
||||
|
||||
return new BufferedReader(new InputStreamReader(getInputStream()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() {
|
||||
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
|
||||
return new ServletInputStream() {
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
|
||||
return bais.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener readListener) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package digital.config.sign.util;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import digital.base.constant.SymbolConstant;
|
||||
import digital.util.util.oConvertUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* http 工具类 获取请求中的参数
|
||||
*
|
||||
* @author jeecg
|
||||
* @date 20210621
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpUtils {
|
||||
|
||||
/**
|
||||
* 将URL的参数和body参数合并
|
||||
*
|
||||
* @author jeecg
|
||||
* @date 20210621
|
||||
* @param request
|
||||
*/
|
||||
public static SortedMap<String, String> getAllParams(HttpServletRequest request) throws IOException {
|
||||
|
||||
SortedMap<String, String> result = new TreeMap<>();
|
||||
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
|
||||
String pathVariable = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/") + 1);
|
||||
if (pathVariable.contains(SymbolConstant.COMMA)) {
|
||||
log.info(" pathVariable: {}",pathVariable);
|
||||
String deString = URLDecoder.decode(pathVariable, "UTF-8");
|
||||
log.info(" pathVariable decode: {}",deString);
|
||||
result.put(SignUtil.X_PATH_VARIABLE, deString);
|
||||
}
|
||||
// 获取URL上的参数
|
||||
Map<String, String> urlParams = getUrlParams(request);
|
||||
for (Map.Entry entry : urlParams.entrySet()) {
|
||||
result.put((String)entry.getKey(), (String)entry.getValue());
|
||||
}
|
||||
Map<String, String> allRequestParam = new HashMap<>(16);
|
||||
// get请求不需要拿body参数
|
||||
if (!HttpMethod.GET.name().equals(request.getMethod())) {
|
||||
allRequestParam = getAllRequestParam(request);
|
||||
}
|
||||
// 将URL的参数和body参数进行合并
|
||||
if (allRequestParam != null) {
|
||||
for (Map.Entry entry : allRequestParam.entrySet()) {
|
||||
result.put((String)entry.getKey(), (String)entry.getValue());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL的参数和body参数合并
|
||||
*
|
||||
* @author jeecg
|
||||
* @date 20210621
|
||||
* @param queryString
|
||||
*/
|
||||
public static SortedMap<String, String> getAllParams(String url, String queryString, byte[] body, String method)
|
||||
throws IOException {
|
||||
|
||||
SortedMap<String, String> result = new TreeMap<>();
|
||||
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
|
||||
String pathVariable = url.substring(url.lastIndexOf("/") + 1);
|
||||
if (pathVariable.contains(SymbolConstant.COMMA)) {
|
||||
log.info(" pathVariable: {}",pathVariable);
|
||||
String deString = URLDecoder.decode(pathVariable, "UTF-8");
|
||||
log.info(" pathVariable decode: {}",deString);
|
||||
result.put(SignUtil.X_PATH_VARIABLE, deString);
|
||||
}
|
||||
// 获取URL上的参数
|
||||
Map<String, String> urlParams = getUrlParams(queryString);
|
||||
for (Map.Entry entry : urlParams.entrySet()) {
|
||||
result.put((String)entry.getKey(), (String)entry.getValue());
|
||||
}
|
||||
Map<String, String> allRequestParam = new HashMap<>(16);
|
||||
// get请求不需要拿body参数
|
||||
if (!HttpMethod.GET.name().equals(method)) {
|
||||
allRequestParam = getAllRequestParam(body);
|
||||
}
|
||||
// 将URL的参数和body参数进行合并
|
||||
if (allRequestParam != null) {
|
||||
for (Map.Entry entry : allRequestParam.entrySet()) {
|
||||
result.put((String)entry.getKey(), (String)entry.getValue());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Body 参数
|
||||
*
|
||||
* @date 15:04 20210621
|
||||
* @param request
|
||||
*/
|
||||
public static Map<String, String> getAllRequestParam(final HttpServletRequest request) throws IOException {
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
|
||||
String str = "";
|
||||
StringBuilder wholeStr = new StringBuilder();
|
||||
// 一行一行的读取body体里面的内容;
|
||||
while ((str = reader.readLine()) != null) {
|
||||
wholeStr.append(str);
|
||||
}
|
||||
// 转化成json对象
|
||||
return JSONObject.parseObject(wholeStr.toString(), Map.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Body 参数
|
||||
*
|
||||
* @date 15:04 20210621
|
||||
* @param body
|
||||
*/
|
||||
public static Map<String, String> getAllRequestParam(final byte[] body) throws IOException {
|
||||
if(body==null){
|
||||
return null;
|
||||
}
|
||||
String wholeStr = new String(body);
|
||||
// 转化成json对象
|
||||
return JSONObject.parseObject(wholeStr.toString(), Map.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL请求参数转换成Map
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public static Map<String, String> getUrlParams(HttpServletRequest request) {
|
||||
Map<String, String> result = new HashMap<>(16);
|
||||
if (oConvertUtils.isEmpty(request.getQueryString())) {
|
||||
return result;
|
||||
}
|
||||
String param = "";
|
||||
try {
|
||||
param = URLDecoder.decode(request.getQueryString(), "utf-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String[] params = param.split("&");
|
||||
for (String s : params) {
|
||||
int index = s.indexOf("=");
|
||||
result.put(s.substring(0, index), s.substring(index + 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL请求参数转换成Map
|
||||
*
|
||||
* @param queryString
|
||||
*/
|
||||
public static Map<String, String> getUrlParams(String queryString) {
|
||||
Map<String, String> result = new HashMap<>(16);
|
||||
if (oConvertUtils.isEmpty(queryString)) {
|
||||
return result;
|
||||
}
|
||||
String param = "";
|
||||
try {
|
||||
param = URLDecoder.decode(queryString, "utf-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String[] params = param.split("&");
|
||||
for (String s : params) {
|
||||
int index = s.indexOf("=");
|
||||
result.put(s.substring(0, index), s.substring(index + 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package digital.config.sign.util;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import digital.base.constant.SymbolConstant;
|
||||
import digital.config.JeecgBaseConfig;
|
||||
import digital.util.exception.JeecgBootException;
|
||||
import digital.util.util.SpringContextUtils;
|
||||
import digital.util.util.oConvertUtils;
|
||||
|
||||
import java.util.SortedMap;
|
||||
|
||||
/**
|
||||
* 签名工具类
|
||||
*
|
||||
* @author jeecg
|
||||
* @date 20210621
|
||||
*/
|
||||
@Slf4j
|
||||
public class SignUtil {
|
||||
public static final String X_PATH_VARIABLE = "x-path-variable";
|
||||
|
||||
/**
|
||||
* @param params
|
||||
* 所有的请求参数都会在这里进行排序加密
|
||||
* @return 验证签名结果
|
||||
*/
|
||||
public static boolean verifySign(SortedMap<String, String> params,String headerSign) {
|
||||
if (params == null || StringUtils.isEmpty(headerSign)) {
|
||||
return false;
|
||||
}
|
||||
// 把参数加密
|
||||
String paramsSign = getParamsSign(params);
|
||||
log.info("Param Sign : {}", paramsSign);
|
||||
return !StringUtils.isEmpty(paramsSign) && headerSign.equals(paramsSign);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param params
|
||||
* 所有的请求参数都会在这里进行排序加密
|
||||
* @return 得到签名
|
||||
*/
|
||||
public static String getParamsSign(SortedMap<String, String> params) {
|
||||
//去掉 Url 里的时间戳
|
||||
params.remove("_t");
|
||||
String paramsJsonStr = JSONObject.toJSONString(params);
|
||||
log.info("Param paramsJsonStr : {}", paramsJsonStr);
|
||||
//设置签名秘钥
|
||||
JeecgBaseConfig jeecgBaseConfig = SpringContextUtils.getBean(JeecgBaseConfig.class);
|
||||
String signatureSecret = jeecgBaseConfig.getSignatureSecret();
|
||||
String curlyBracket = SymbolConstant.DOLLAR + SymbolConstant.LEFT_CURLY_BRACKET;
|
||||
if(oConvertUtils.isEmpty(signatureSecret) || signatureSecret.contains(curlyBracket)){
|
||||
throw new JeecgBootException("签名密钥 ${jeecg.signatureSecret} 缺少配置 !!");
|
||||
}
|
||||
return DigestUtils.md5DigestAsHex((paramsJsonStr + signatureSecret).getBytes()).toUpperCase();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package digital.config.vo;
|
||||
|
||||
/**
|
||||
* @Description: TODO
|
||||
* @author: scott
|
||||
* @date: 2022年01月21日 14:23
|
||||
*/
|
||||
public class Shiro {
|
||||
private String excludeUrls = "";
|
||||
|
||||
public String getExcludeUrls() {
|
||||
return excludeUrls;
|
||||
}
|
||||
|
||||
public void setExcludeUrls(String excludeUrls) {
|
||||
this.excludeUrls = excludeUrls;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user