Browse Source

创建项目

master
刘力 3 years ago
parent
commit
28644b0c20
  1. 103
      admin/pom.xml
  2. 18
      admin/src/main/java/com/canvas/web/AdminApplication.java
  3. 11
      admin/src/main/java/com/canvas/web/controller/TestController.java
  4. 126
      admin/src/main/resources/application-dev.yml
  5. 27
      admin/src/main/resources/application.yml
  6. 51
      common/pom.xml
  7. 11
      common/src/main/java/com/canvas/web/annotation/AnonymousAccess.java
  8. 59
      common/src/main/java/com/canvas/web/annotation/Query.java
  9. 59
      common/src/main/java/com/canvas/web/annotation/rest/AnonymousGetMapping.java
  10. 38
      common/src/main/java/com/canvas/web/base/BaseDTO.java
  11. 41
      common/src/main/java/com/canvas/web/config/CommonConfig.java
  12. 38
      common/src/main/java/com/canvas/web/config/UploadFileConfig.java
  13. 19
      common/src/main/java/com/canvas/web/service/IUplpadService.java
  14. 30
      common/src/main/java/com/canvas/web/service/impl/UploadServiceImpl.java
  15. 39
      common/src/main/java/com/canvas/web/utils/CacheKey.java
  16. 13
      common/src/main/java/com/canvas/web/utils/CallBack.java
  17. 294
      common/src/main/java/com/canvas/web/utils/CommonUtils.java
  18. 26
      common/src/main/java/com/canvas/web/utils/ElAdminConstant.java
  19. 42
      common/src/main/java/com/canvas/web/utils/ErrorCode.java
  20. 356
      common/src/main/java/com/canvas/web/utils/FileUtil.java
  21. 112
      common/src/main/java/com/canvas/web/utils/JsonResult.java
  22. 696
      common/src/main/java/com/canvas/web/utils/RedisUtils.java
  23. 121
      common/src/main/java/com/canvas/web/utils/SpringContextHolder.java
  24. 268
      common/src/main/java/com/canvas/web/utils/StringUtils.java
  25. 305
      common/src/main/java/com/canvas/web/utils/UploadUtils.java
  26. 287
      pom.xml
  27. 58
      system/pom.xml
  28. 45
      system/src/main/java/AppRun.java
  29. 24
      system/src/main/java/modules/security/config/ConfigBeanConfiguration.java
  30. 31
      system/src/main/java/modules/security/config/SpringSecurityConfig.java
  31. 48
      system/src/main/java/modules/security/config/bean/LoginCode.java
  32. 25
      system/src/main/java/modules/security/config/bean/LoginCodeEnum.java
  33. 91
      system/src/main/java/modules/security/config/bean/LoginProperties.java
  34. 52
      system/src/main/java/modules/security/config/bean/SecurityProperties.java
  35. 19
      system/src/main/java/modules/security/security/JwtAccessDeniedHandler.java
  36. 19
      system/src/main/java/modules/security/security/JwtAuthenticationEntryPoint.java
  37. 51
      system/src/main/java/modules/security/security/SecurityProperties.java
  38. 16
      system/src/main/java/modules/security/security/TokenConfigurer.java
  39. 107
      system/src/main/java/modules/security/security/TokenProvider.java
  40. 19
      system/src/main/java/modules/security/service/OnlineUserService.java
  41. 31
      system/src/main/java/modules/security/service/UserCacheClean.java
  42. 74
      system/src/main/java/modules/security/service/UserDetailsServiceImpl.java
  43. 22
      system/src/main/java/modules/security/service/dto/AuthUserDto.java
  44. 65
      system/src/main/java/modules/security/service/dto/JwtUserDto.java
  45. 15
      system/src/main/java/modules/system/service/DataService.java
  46. 16
      system/src/main/java/modules/system/service/RoleService.java
  47. 98
      system/src/main/java/modules/system/service/UserService.java
  48. 76
      system/src/main/java/modules/system/service/dto/MenuDto.java
  49. 45
      system/src/main/java/modules/system/service/dto/RoleDto.java
  50. 16
      system/src/main/java/modules/system/service/dto/RoleSmallDto.java
  51. 50
      system/src/main/java/modules/system/service/dto/UserDto.java
  52. 32
      system/src/main/java/modules/system/service/dto/UserQueryCriteria.java
  53. 115
      system/src/main/resources/config/application-dev.yml
  54. 0
      system/src/main/resources/config/application-prod.yml
  55. 60
      system/src/main/resources/config/application.yml

103
admin/pom.xml

@ -1,103 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>yxk_canvasScreen</artifactId>
<groupId>com.canvas.web</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>admin</artifactId>
<name>启动入口</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<!-- 依赖声明 -->
<dependencies>
<!-- 核心模块 -->
<dependency>
<groupId>com.canvas.web</groupId>
<artifactId>system</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 代码生成 -->
<dependency>
<groupId>com.canvas.web</groupId>
<artifactId>generator</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 定时任务 -->
<dependency>
<groupId>com.canvas.web</groupId>
<artifactId>quartz</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 消息队列 -->
<dependency>
<groupId>com.canvas.web</groupId>
<artifactId>queue</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 引入阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.11.RELEASE</version>
<configuration>
<finalName>JavaWeb_Vue</finalName>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

18
admin/src/main/java/com/canvas/web/AdminApplication.java

@ -1,18 +0,0 @@
package com.canvas.web;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication(scanBasePackages = {"com.canvas.*"})
@EnableTransactionManagement
@EnableScheduling
public class AdminApplication {
public static void main(String[] args){
SpringApplication.run(AdminApplication.class,args);
System.out.println("多媒体后台管理系统启动成功!");
}
}

11
admin/src/main/java/com/canvas/web/controller/TestController.java

@ -1,11 +0,0 @@
package com.canvas.web.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//业务测试接口
@RestController
@RequestMapping("/test")
public class TestController {
}

126
admin/src/main/resources/application-dev.yml

@ -1,126 +0,0 @@
#自定义配置
canvasweb:
image-url: https://images.canvase.com/
app-debug: true
spring:
# 配置数据源
datasource:
# 使用阿里的Druid连接池
druid:
db-type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
url: jdbc:mysql://192.168.99.207:3306/javaweb.vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false
username: root
password: ftzn83560792
# 连接池的配置信息
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 5
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
# 配置DruidStatFilter
webStatFilter:
enabled: true
url-pattern: "/*"
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
# 配置DruidStatViewServlet
statViewServlet:
url-pattern: "/druid/*"
# IP白名单(没有配置或者为空,则允许所有访问)
allow: 127.0.0.1,192.168.163.1
# IP黑名单 (存在共同时,deny优先于allow)
deny: 192.168.1.73
# 禁用HTML页面上的“Reset All”功能
reset-enable: false
# 登录名
login-username: admin
# 登录密码
login-password: 123456
# Redis数据源
redis:
# 缓存库默认索引0
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间(毫秒)
timeout: 6000
# 默认的数据过期时间,主要用于shiro权限管理
expire: 2592000
jedis:
pool:
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 1 # 连接池中的最小空闲连接
file:
#上传的服务器上的映射文件夹
accessPath: /uploads/
#静态资源对外暴露的访问路径
staticAccessPath: /**
#静态资源实际存储路径
uploadFolder: E:\JavaWeb_Vue\JavaWeb\uploads\
# Shiro
shiro:
cipher-key: f/SX5TIve5WWzT4aQlABJA==
cookie-name: shiro-cookie2
user:
# 登录地址
loginUrl: /login
# 权限认证失败地址
unauthorizedUrl: /unauth
# 首页地址
indexUrl: /index
# 验证码开关
captchaEnabled: true
# 验证码类型 math 数组计算 char 字符
captchaType: math
cookie:
# 设置Cookie的域名 默认空,即当前访问的域名
domain:
# 设置cookie的有效访问路径
path: /
# 设置HttpOnly属性
httpOnly: true
# 设置Cookie的过期时间,天为单位
maxAge: 30
session:
# Session超时时间(默认30分钟)
expireTime: 300
# 同步session到数据库的周期(默认1分钟)
dbSyncPeriod: 1
# 相隔多久检查一次session的有效性,默认就是10分钟
validationInterval: 10

27
admin/src/main/resources/application.yml

@ -1,27 +0,0 @@
server:
port: 9030
servlet:
context-path: /api
spring:
profiles:
active: dev
#配置 Jpa
jpa:
properties:
hibernate:
ddl-auto: none
open-in-view: true
task:
pool:
# 核心线程池大小
core-pool-size: 10
# 最大线程数
max-pool-size: 30
# 活跃时间
keep-alive-seconds: 60
# 队列容量
queue-capacity: 50

51
common/pom.xml

@ -13,22 +13,12 @@
<name>公共模块</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<hutool.version>5.3.4</hutool.version>
</properties>
<!-- 依赖声明 -->
<dependencies>
<!--Spring boot 核心-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- WEB 模块依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Redis 起始依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -40,11 +30,7 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
@ -58,12 +44,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- UserAgent工具类 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.21</version>
</dependency>
<!-- Excel依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
@ -104,17 +85,7 @@
<artifactId>commons-compress</artifactId>
<version>1.21</version>
</dependency>
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.18.2</version>
</dependency>
<!-- 二维码生成依赖 -->
<dependency>
<groupId>com.google.zxing</groupId>
@ -132,6 +103,18 @@
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
</dependencies>
</project>

11
common/src/main/java/com/canvas/web/annotation/AnonymousAccess.java

@ -0,0 +1,11 @@
package com.canvas.web.annotation;
import java.lang.annotation.*;
@Inherited
@Documented
@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonymousAccess {
}

59
common/src/main/java/com/canvas/web/annotation/Query.java

@ -0,0 +1,59 @@
package com.canvas.web.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Query {
//基本对象的属性名
String propName() default "";
//查询方式
Type type() default Type.EQUAL;
//连接查询的属性名如User类中的dept
String joinName() default "";
//默认左连接
Join join() default Join.LEFT;
//多字段模糊搜索仅支持String类型字段多个用逗号隔开例如@Query(blurry="email,username")
String blurry() default "";
enum Type {
// 相等
EQUAL
// 大于等于
, GREATER_THAN
// 小于等于
, LESS_THAN
// 中模糊查询
, INNER_LIKE
// 左模糊查询
, LEFT_LIKE
// 右模糊查询
, RIGHT_LIKE
// 小于
, LESS_THAN_NQ
// 包含
, IN
// 不等于
,NOT_EQUAL
// between
,BETWEEN
// 不为空
,NOT_NULL
// 为空
,IS_NULL
}
//适用于简单连接查询复杂的查询适用sql或其他注解
enum Join {
LEFT, RIGHT, INNER
}
}

59
common/src/main/java/com/canvas/web/annotation/rest/AnonymousGetMapping.java

@ -0,0 +1,59 @@
package com.canvas.web.annotation.rest;
import com.canvas.web.annotation.AnonymousAccess;
import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.lang.annotation.*;
@AnonymousAccess
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface AnonymousGetMapping {
/**
* Alias for {@link RequestMapping#name}.
*/
@AliasFor(annotation = RequestMapping.class)
String name() default "";
/**
* Alias for {@link RequestMapping#value}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
/**
* Alias for {@link RequestMapping#path}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] path() default {};
/**
* Alias for {@link RequestMapping#params}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] params() default {};
/**
* Alias for {@link RequestMapping#headers}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] headers() default {};
/**
* Alias for {@link RequestMapping#consumes}.
*
* @since 4.3.5
*/
@AliasFor(annotation = RequestMapping.class)
String[] consumes() default {};
/**
* Alias for {@link RequestMapping#produces}.
*/
@AliasFor(annotation = RequestMapping.class)
String[] produces() default {};
}

38
common/src/main/java/com/canvas/web/base/BaseDTO.java

@ -0,0 +1,38 @@
package com.canvas.web.base;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.builder.ToStringBuilder;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.Timestamp;
@Getter
@Setter
public class BaseDTO implements Serializable {
private String createBy;
private String updatedBy;
private Timestamp createTime;
private Timestamp updateTime;
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this);
Field[] fields = this.getClass().getDeclaredFields();
try {
for (Field f : fields) {
f.setAccessible(true);
builder.append(f.getName(), f.get(this)).append("\n");
}
} catch (Exception e) {
builder.append("toString builder encounter an error");
}
return builder.toString();
}
}

41
common/src/main/java/com/canvas/web/config/CommonConfig.java

@ -0,0 +1,41 @@
package com.canvas.web.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class CommonConfig {
/**
* 图片域名
*/
public static String imageURL;
/**
* 是否演示环境true是,false否
*/
public static boolean appDebug;
/**
* 图片域名赋值
*
* @param url 域名地址
*/
@Value("${javaweb.image-url}")
public void setImageURL(String url) {
imageURL = url;
}
/**
* 是否演示模式
*
* @param debug
*/
@Value("${javaweb.app-debug}")
public void setAppDebug(boolean debug) {
appDebug = debug;
}
}

38
common/src/main/java/com/canvas/web/config/UploadFileConfig.java

@ -0,0 +1,38 @@
package com.canvas.web.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class UploadFileConfig {
/**
* 上传目录
*/
public static String uploadFolder;
/**
* 访问路径
*/
public static String staticAccessPath;
/**
* 上传服务器的映射文件夹
*/
public static String accessPath;
@Value("${file.uploadFolder}")
public void setUploadFolder(String path) {
uploadFolder = path;
}
@Value("${file.staticAccessPath}")
public void setStaticAccessPath(String path) {
staticAccessPath = path;
}
@Value("${file.accessPath}")
public void setAccessPath(String path) {
accessPath = path;
}
}

19
common/src/main/java/com/canvas/web/service/IUplpadService.java

@ -0,0 +1,19 @@
package com.canvas.web.service;
import com.canvas.web.utils.JsonResult;
import javax.servlet.http.HttpServletRequest;
public interface IUplpadService {
/**
* 上传图片
*
* @param request 网络请求
* @param name 目录名
* @return
*/
JsonResult uploadImage(HttpServletRequest request, String name);
}

30
common/src/main/java/com/canvas/web/service/impl/UploadServiceImpl.java

@ -0,0 +1,30 @@
package com.canvas.web.service.impl;
import com.canvas.web.service.IUplpadService;
import com.canvas.web.utils.CommonUtils;
import com.canvas.web.utils.JsonResult;
import com.canvas.web.utils.UploadUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
public class UploadServiceImpl implements IUplpadService {
/**
* 上传图片
*
* @param request 网络请求
* @param name 目录名
* @return
*/
@Override
public JsonResult uploadImage(HttpServletRequest request, String name) {
UploadUtils uploadUtils = new UploadUtils();
Map<String, Object> result = uploadUtils.uploadFile(request, name);
List<String> imageList = (List<String>) result.get("image");
String imageUrl = CommonUtils.getImageURL(imageList.get(0));
return JsonResult.success("上传成功", imageUrl);
}
}

39
common/src/main/java/com/canvas/web/utils/CacheKey.java

@ -0,0 +1,39 @@
package com.canvas.web.utils;
/**
* 关于缓存的Key集合
*/
public interface CacheKey {
/**
* 内置 用户岗位应用菜单角色 相关key
*/
String USER_MODIFY_TIME_KEY = "user:modify:time:key:";
String APP_MODIFY_TIME_KEY = "app:modify:time:key:";
String JOB_MODIFY_TIME_KEY = "job:modify:time:key:";
String MENU_MODIFY_TIME_KEY = "menu:modify:time:key:";
String ROLE_MODIFY_TIME_KEY = "role:modify:time:key:";
String DEPT_MODIFY_TIME_KEY = "dept:modify:time:key:";
/**
* 用户
*/
String USER_ID = "user::id:";
String USER_NAME = "user::username:";
/**
* 数据
*/
String DATE_USER = "data::user:";
/**
* 菜单
*/
String MENU_USER = "menu::user:";
/**
* 角色授权
*/
String ROLE_AUTH = "role::auth:";
/**
* 角色信息
*/
String ROLE_ID = "role::id:";
}

13
common/src/main/java/com/canvas/web/utils/CallBack.java

@ -0,0 +1,13 @@
package com.canvas.web.utils;
public interface CallBack {
//回调执行方法
void executor();
//本回调任务名称
default String getCallBackName() {
return Thread.currentThread().getId() + ":" + this.getClass().getName();
}
}

294
common/src/main/java/com/canvas/web/utils/CommonUtils.java

@ -0,0 +1,294 @@
package com.canvas.web.utils;
import com.alibaba.fastjson.JSONObject;
import com.canvas.web.config.CommonConfig;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.security.MessageDigest;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CommonUtils {
/**
* 分转元
*
* @param amount
* @return
*/
public static String fenToYuan(String amount) {
NumberFormat format = NumberFormat.getInstance();
try {
Number number = format.parse(amount);
double temp = number.doubleValue() / 100.0;
format.setGroupingUsed(false);
// 设置返回的小数部分所允许的最大位数
format.setMaximumFractionDigits(2);
amount = format.format(temp);
} catch (ParseException e) {
e.printStackTrace();
}
return amount;
}
/**
* 元转分
*
* @param amount
* @return
*/
public static String yuanToFen(String amount) {
NumberFormat format = NumberFormat.getInstance();
try {
Number number = format.parse(amount);
double temp = number.doubleValue() * 100.0;
format.setGroupingUsed(false);
// 设置返回数的小数部分所允许的最大位数
format.setMaximumFractionDigits(0);
amount = format.format(temp);
} catch (ParseException e) {
e.printStackTrace();
}
return amount;
}
/**
* 获取当前时间时间戳
*
* @return
*/
public static Integer timeStamp() {
long currentTime = System.currentTimeMillis();
String time = String.valueOf(currentTime / 1000);
return Integer.valueOf(time);
}
/**
* 时间转为日期格式
*
* @param time
* @param format
* @return
*/
public static String formatTime(Integer time, String format) {
if (StringUtils.isEmpty(time)) {
return "";
}
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
long timeLong = time.longValue() * 1000;
Date date = new Date(timeLong);
return dateFormat.format(date);
}
/**
* 获取到图片域名的地址
*
* @param imageUrl
* @return
*/
public static String getImageURL(String imageUrl) {
return CommonConfig.imageURL + imageUrl;
}
/**
* 验证邮箱是否正确
*
* @param email
* @return
*/
public static boolean isEmail(String email) {
boolean flag = false;
try {
String check = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
Pattern regex = Pattern.compile(check);
Matcher matcher = regex.matcher(email);
flag = matcher.matches();
} catch (Exception e) {
flag = false;
}
return flag;
}
/**
* 验证手机号是否正确
*
* @param mobile
* @return
*/
public static boolean isMobile(String mobile) {
boolean flag = false;
try {
Pattern p = Pattern.compile("^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$");
Matcher m = p.matcher(mobile);
flag = m.matches();
} catch (Exception e) {
flag = false;
}
return flag;
}
/**
* 生成指定位数的随机字符串
*
* @param isNum 是否是纯数字
* @param length 长度
* @return
*/
public static String getRandomStr(boolean isNum, int length) {
String resultStr = "";
String str = isNum ? "1234567890" : "1234567890abcdefghijkmnpqrstuvwxyz";
int len = str.length();
boolean isStop = true;
do {
resultStr = "";
int count = 0;
for (int i = 0; i < length; i++) {
double dblR = Math.random() * len;
int intR = (int) Math.floor(dblR);
char c = str.charAt(intR);
if (('0' <= c) && (c <= '9')) {
count++;
}
resultStr += str.charAt(intR);
}
if (count >= 2) {
isStop = false;
}
} while (isStop);
return resultStr;
}
/**
* 判断是否在数组中
*
* @param s
* @param array
* @return
*/
public static boolean inArray(final String s, final String[] array) {
for (String item : array) {
if (item != null && item.equals(s)) {
return true;
}
}
return false;
}
/**
* 从html中提取纯文本
*
* @param strHtml
* @return
*/
public static String stripHtml(String strHtml) {
String content = strHtml.replaceAll("</?[^>]+>", ""); //剔出<html>的标签
content = content.replaceAll("\\s*|\t|\r|\n", "");//去除字符串中的空格,回车,换行符,制表符
return content;
}
/**
* 去除字符串中的空格回车换行符制表符等
*
* @param str 原始字符串
* @return
*/
public static String replaceSpecialStr(String str) {
String repl = "";
if (str != null) {
Pattern p = Pattern.compile("\\s*|\t|\r|\n");
Matcher m = p.matcher(str);
repl = m.replaceAll("");
}
return repl;
}
/**
* 判断某个元素是否在数组中
*
* @param key 元素
* @param map 数组
* @return
*/
public static boolean inArray(String key, Map<String, String> map) {
boolean flag = false;
for (String k : map.keySet()) {
if (k.equals(key)) {
flag = true;
}
}
return flag;
}
/**
* 对象转Map
*
* @param obj 对象
* @return
* @throws IllegalAccessException
*/
public static Map<String, Object> objectToMap(Object obj) throws IllegalAccessException {
Map<String, Object> map = new HashMap<>();
Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
String fieldName = field.getName();
Object value = field.get(obj);
map.put(fieldName, value);
}
return map;
}
/**
* 判断是否是JSON格式
*
* @param str JSON字符串
* @return
*/
private boolean isJson(String str) {
try {
JSONObject jsonStr = JSONObject.parseObject(str);
return true;
} catch (Exception e) {
return false;
}
}
/**
* MD5方法
*
* @param source
* @return
*/
public static String md5(byte[] source) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(source);
StringBuffer buf = new StringBuffer();
for (byte b : md.digest()) {
buf.append(String.format("%02x", b & 0xff));
}
return buf.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 密码加密
*
* @param password 密码
* @return
*/
public static String password(String password) {
String md51 = md5(password.getBytes());
String pwd = md5((md51 + "IgtUdEQJyVevaCxQnY").getBytes());
return pwd;
}
}

26
common/src/main/java/com/canvas/web/utils/ElAdminConstant.java

@ -0,0 +1,26 @@
package com.canvas.web.utils;
public class ElAdminConstant {
/**
* 用于IP定位转换
*/
public static final String REGION = "内网IP|内网IP";
/**
* win 系统
*/
public static final String WIN = "win";
/**
* mac 系统
*/
public static final String MAC = "mac";
/**
* 常用接口
*/
public static class Url {
// IP归属地查询
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true";
}
}

42
common/src/main/java/com/canvas/web/utils/ErrorCode.java

@ -0,0 +1,42 @@
package com.canvas.web.utils;
public enum ErrorCode {
FAILED(1, "操作失败"),
TOKEN_MISSING(300, "token丢失"),
TOKEN_ERROR(301, "token认证失败"),
PARAM_MISSING(400, "参数丢失"),
PARAM_ERROR(401, "参数错误"),
SYSTEM_ERROR(500, "系统错误"),
UNKNOWN_ERROR(501, "未知错误");
public static final Integer MESSAGE_PARAM_MISSING = 400;
/**
* 错误码
*/
private Integer code;
/**
* 错误描述
*/
private String msg;
public Integer getCode() {
return this.code;
}
public String getMsg() {
return this.msg;
}
/**
* 构造函数
*
* @param code
* @param msg
*/
private ErrorCode(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
}

356
common/src/main/java/com/canvas/web/utils/FileUtil.java

@ -0,0 +1,356 @@
package com.canvas.web.utils;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import com.canvas.web.exception.BaseException;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class FileUtil extends cn.hutool.core.io.FileUtil{
private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
/**
* 系统临时目录
* <br>
* windows 包含路径分割符但Linux 不包含,
* 在windows \\==\ 前提下
* 为安全起见 同意拼装 路径分割符
* <pre>
* java.io.tmpdir
* windows : C:\Users/xxx\AppData\Local\Temp\
* linux: /temp
* </pre>
*/
public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator;
/**
* 定义GB的计算常量
*/
private static final int GB = 1024 * 1024 * 1024;
/**
* 定义MB的计算常量
*/
private static final int MB = 1024 * 1024;
/**
* 定义KB的计算常量
*/
private static final int KB = 1024;
/**
* 格式化小数
*/
private static final DecimalFormat DF = new DecimalFormat("0.00");
public static final String IMAGE = "图片";
public static final String TXT = "文档";
public static final String MUSIC = "音乐";
public static final String VIDEO = "视频";
public static final String OTHER = "其他";
/**
* MultipartFile转File
*/
public static File toFile(MultipartFile multipartFile) {
// 获取文件名
String fileName = multipartFile.getOriginalFilename();
// 获取文件后缀
String prefix = "." + getExtensionName(fileName);
File file = null;
try {
// 用uuid作为文件名防止生成的临时文件重复
file = File.createTempFile(IdUtil.simpleUUID(), prefix);
// MultipartFile to File
multipartFile.transferTo(file);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return file;
}
/**
* 获取文件扩展名不带 .
*/
public static String getExtensionName(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length() - 1))) {
return filename.substring(dot + 1);
}
}
return filename;
}
/**
* Java文件操作 获取不带扩展名的文件名
*/
public static String getFileNameNoEx(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length()))) {
return filename.substring(0, dot);
}
}
return filename;
}
/**
* 文件大小转换
*/
public static String getSize(long size) {
String resultSize;
if (size / GB >= 1) {
//如果当前Byte的值大于等于1GB
resultSize = DF.format(size / (float) GB) + "GB ";
} else if (size / MB >= 1) {
//如果当前Byte的值大于等于1MB
resultSize = DF.format(size / (float) MB) + "MB ";
} else if (size / KB >= 1) {
//如果当前Byte的值大于等于1KB
resultSize = DF.format(size / (float) KB) + "KB ";
} else {
resultSize = size + "B ";
}
return resultSize;
}
/**
* inputStream File
*/
static File inputStreamToFile(InputStream ins, String name) throws Exception {
File file = new File(SYS_TEM_DIR + name);
if (file.exists()) {
return file;
}
OutputStream os = new FileOutputStream(file);
int bytesRead;
int len = 8192;
byte[] buffer = new byte[len];
while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
return file;
}
/**
* 将文件名解析成文件的上传路径
*/
public static File upload(MultipartFile file, String filePath) {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS");
String name = getFileNameNoEx(file.getOriginalFilename());
String suffix = getExtensionName(file.getOriginalFilename());
String nowStr = "-" + format.format(date);
try {
String fileName = name + nowStr + "." + suffix;
String path = filePath + fileName;
// getCanonicalFile 可解析正确各种路径
File dest = new File(path).getCanonicalFile();
// 检测是否存在目录
if (!dest.getParentFile().exists()) {
if (!dest.getParentFile().mkdirs()) {
System.out.println("was not successful.");
}
}
// 文件写入
file.transferTo(dest);
return dest;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* 导出excel
*/
public static void downloadExcel(List<Map<String, Object>> list, HttpServletResponse response) throws IOException {
String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx";
File file = new File(tempPath);
BigExcelWriter writer = ExcelUtil.getBigWriter(file);
// 一次性写出内容使用默认样式强制输出标题
writer.write(list, true);
SXSSFSheet sheet = (SXSSFSheet)writer.getSheet();
//上面需要强转SXSSFSheet 不然没有trackAllColumnsForAutoSizing方法
sheet.trackAllColumnsForAutoSizing();
//列宽自适应
writer.autoSizeColumnAll();
//列宽自适应支持中文单元格
sizeChineseColumn(sheet, writer);
//response为HttpServletResponse对象
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
//test.xls是弹出下载对话框的文件名不能为中文中文请自行编码
response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
ServletOutputStream out = response.getOutputStream();
// 终止后删除临时文件
file.deleteOnExit();
writer.flush(out, true);
//此处记得关闭输出Servlet流
IoUtil.close(out);
}
/**
* 自适应宽度(中文支持)
*/
private static void sizeChineseColumn(SXSSFSheet sheet, BigExcelWriter writer) {
for (int columnNum = 0; columnNum < writer.getColumnCount(); columnNum++) {
int columnWidth = sheet.getColumnWidth(columnNum) / 256;
for (int rowNum = 0; rowNum < sheet.getLastRowNum(); rowNum++) {
SXSSFRow currentRow;
if (sheet.getRow(rowNum) == null) {
currentRow = sheet.createRow(rowNum);
} else {
currentRow = sheet.getRow(rowNum);
}
if (currentRow.getCell(columnNum) != null) {
SXSSFCell currentCell = currentRow.getCell(columnNum);
if (currentCell.getCellTypeEnum() == CellType.STRING) {
int length = currentCell.getStringCellValue().getBytes().length;
if (columnWidth < length) {
columnWidth = length;
}
}
}
}
sheet.setColumnWidth(columnNum, columnWidth * 256);
}
}
public static String getFileType(String type) {
String documents = "txt doc pdf ppt pps xlsx xls docx";
String music = "mp3 wav wma mpa ram ra aac aif m4a";
String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
if (image.contains(type)) {
return IMAGE;
} else if (documents.contains(type)) {
return TXT;
} else if (music.contains(type)) {
return MUSIC;
} else if (video.contains(type)) {
return VIDEO;
} else {
return OTHER;
}
}
public static void checkSize(long maxSize, long size) {
// 1M
int len = 1024 * 1024;
if (size > (maxSize * len)) {
throw new BaseException("文件超出规定大小");
}
}
/**
* 判断两个文件是否相同
*/
public static boolean check(File file1, File file2) {
String img1Md5 = getMd5(file1);
String img2Md5 = getMd5(file2);
return img1Md5.equals(img2Md5);
}
/**
* 判断两个文件是否相同
*/
public static boolean check(String file1Md5, String file2Md5) {
return file1Md5.equals(file2Md5);
}
private static byte[] getByte(File file) {
// 得到文件长度
byte[] b = new byte[(int) file.length()];
try {
InputStream in = new FileInputStream(file);
try {
System.out.println(in.read(b));
} catch (IOException e) {
log.error(e.getMessage(), e);
}
} catch (FileNotFoundException e) {
log.error(e.getMessage(), e);
return null;
}
return b;
}
private static String getMd5(byte[] bytes) {
// 16进制字符
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(bytes);
byte[] md = mdTemp.digest();
int j = md.length;
char[] str = new char[j * 2];
int k = 0;
// 移位 输出字符串
for (byte byte0 : md) {
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* 下载文件
*
* @param request /
* @param response /
* @param file /
*/
public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) {
response.setCharacterEncoding(request.getCharacterEncoding());
response.setContentType("application/octet-stream");
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
IOUtils.copy(fis, response.getOutputStream());
response.flushBuffer();
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
if (fis != null) {
try {
fis.close();
if (deleteOnExit) {
file.deleteOnExit();
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
}
public static String getMd5(File file) {
return getMd5(getByte(file));
}
}

112
common/src/main/java/com/canvas/web/utils/JsonResult.java

@ -0,0 +1,112 @@
package com.canvas.web.utils;
import org.springframework.http.HttpStatus;
import java.io.Serializable;
public class JsonResult implements Serializable {
// 错误码
private Integer code= HttpStatus.OK.value();
//提示语
private String msg= "操作成功";
//返回对象
private Object data;
public Integer getCode() {
return this.code;
}
public void setCode(final Integer code) {
this.code = code;
}
public String getMsg() {
return this.msg;
}
public void setMsg(final String msg) {
this.msg = msg;
}
public Object getData() {
return this.data;
}
public void setData(final Object data) {
this.data = data;
}
/**
* 构造函数
*/
public JsonResult() {
}
public JsonResult(String msg) {
this.msg = msg;
}
public JsonResult(Object data) {
this.data = data;
}
public JsonResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public JsonResult(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static JsonResult success() {
return new JsonResult();
}
public static JsonResult success(String msg) {
return new JsonResult(msg);
}
public JsonResult success(Object data) {
return new JsonResult(HttpStatus.OK.value(), msg, data);
}
public static JsonResult success(String msg, Object data) {
return new JsonResult(HttpStatus.OK.value(), msg, data);
}
public static JsonResult error() {
return new JsonResult(-1, "操作失败");
}
public static JsonResult error(String msg) {
return new JsonResult(-1, msg);
}
public static JsonResult error(Integer code, String msg) {
return new JsonResult(code, msg);
}
public static JsonResult error(Integer code, String msg, Object data) {
return new JsonResult(code, msg, data);
}
public static JsonResult error(ErrorCode errorCode) {
return new JsonResult(errorCode.getCode(), errorCode.getMsg());
}
public static JsonResult error(HttpStatus httpStatus, String msg, Object data) {
return new JsonResult(httpStatus.value(), msg, data);
}
public Object error(HttpStatus httpStatus, String msg) {
this.code = httpStatus.value();
this.msg = msg;
return this;
}
}

696
common/src/main/java/com/canvas/web/utils/RedisUtils.java

@ -0,0 +1,696 @@
package com.canvas.web.utils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtils {
private static final Logger log = LoggerFactory.getLogger(RedisUtils.class);
private RedisTemplate<Object, Object> redisTemplate;
@Value("${jwt.online-key}")
private String onlineKey;
public RedisUtils(RedisTemplate<Object, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 指定缓存失效时间
*
* @param key
* @param time 时间()
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
return true;
}
/**
* 指定缓存失效时间
*
* @param key
* @param time 时间()
* @param timeUnit 单位
*/
public boolean expire(String key, long time, TimeUnit timeUnit) {
try {
if (time > 0) {
redisTemplate.expire(key, time, timeUnit);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
return true;
}
/**
* 根据 key 获取过期时间
*
* @param key 不能为null
* @return 时间() 返回0代表为永久有效
*/
public long getExpire(Object key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 查找匹配key
*
* @param pattern key
* @return /
*/
public List<String> scan(String pattern) {
ScanOptions options = ScanOptions.scanOptions().match(pattern).build();
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection rc = Objects.requireNonNull(factory).getConnection();
Cursor<byte[]> cursor = rc.scan(options);
List<String> result = new ArrayList<>();
while (cursor.hasNext()) {
result.add(new String(cursor.next()));
}
try {
RedisConnectionUtils.releaseConnection(rc, factory);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return result;
}
/**
* 分页查询 key
*
* @param patternKey key
* @param page 页码
* @param size 每页数目
* @return /
*/
public List<String> findKeysForPage(String patternKey, int page, int size) {
ScanOptions options = ScanOptions.scanOptions().match(patternKey).build();
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection rc = Objects.requireNonNull(factory).getConnection();
Cursor<byte[]> cursor = rc.scan(options);
List<String> result = new ArrayList<>(size);
int tmpIndex = 0;
int fromIndex = page * size;
int toIndex = page * size + size;
while (cursor.hasNext()) {
if (tmpIndex >= fromIndex && tmpIndex < toIndex) {
result.add(new String(cursor.next()));
tmpIndex++;
continue;
}
// 获取到满足条件的数据后,就可以退出了
if (tmpIndex >= toIndex) {
break;
}
tmpIndex++;
cursor.next();
}
try {
RedisConnectionUtils.releaseConnection(rc, factory);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return result;
}
/**
* 判断key是否存在
*
* @param key
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 删除缓存
*
* @param keys 可以传一个值 或多个
*/
public void del(String... keys) {
if (keys != null && keys.length > 0) {
if (keys.length == 1) {
boolean result = redisTemplate.delete(keys[0]);
log.debug("--------------------------------------------");
log.debug(new StringBuilder("删除缓存:").append(keys[0]).append(",结果:").append(result).toString());
log.debug("--------------------------------------------");
} else {
Set<Object> keySet = new HashSet<>();
for (String key : keys) {
keySet.addAll(redisTemplate.keys(key));
}
long count = redisTemplate.delete(keySet);
log.debug("--------------------------------------------");
log.debug("成功删除缓存:" + keySet.toString());
log.debug("缓存删除数量:" + count + "个");
log.debug("--------------------------------------------");
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key
* @return
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 批量获取
*
* @param keys
* @return
*/
public List<Object> multiGet(List<String> keys) {
List list = redisTemplate.opsForValue().multiGet(Sets.newHashSet(keys));
List resultList = Lists.newArrayList();
Optional.ofNullable(list).ifPresent(e-> list.forEach(ele-> Optional.ofNullable(ele).ifPresent(resultList::add)));
return resultList;
}
/**
* 普通缓存放入
*
* @param key
* @param value
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key
* @param value
* @param time 时间() time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key
* @param value
* @param time 时间
* @param timeUnit 类型
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, timeUnit);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
// ================================Map=================================
/**
* HashGet
*
* @param key 不能为null
* @param item 不能为null
* @return
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key
* @param map 对应多个键值
* @param time 时间()
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key
* @param item
* @param value
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key
* @param item
* @param value
* @param time 时间() 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 不能为null
* @param item 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 不能为null
* @param item 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key
* @param item
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key
* @param item
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key
* @param value
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key
* @param values 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key
* @param time 时间()
* @param values 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
/**
* 移除值为value的
*
* @param key
* @param values 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key
* @param start 开始
* @param end 结束 0 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key
* @param index 索引 index>=0时 0 表头1 第二个元素依次类推index<0时-1表尾-2倒数第二个元素依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 将list放入缓存
*
* @param key
* @param value
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 将list放入缓存
*
* @param key
* @param value
* @param time 时间()
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 将list放入缓存
*
* @param key
* @param value
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 将list放入缓存
*
* @param key
* @param value
* @param time 时间()
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key
* @param index 索引
* @param value
* @return /
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 移除N个值为value
*
* @param key
* @param count 移除多少个
* @param value
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
return redisTemplate.opsForList().remove(key, count, value);
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
}
}
/**
* @param prefix 前缀
* @param ids id
*/
public void delByKeys(String prefix, Set<Long> ids) {
Set<Object> keys = new HashSet<>();
for (Long id : ids) {
keys.addAll(redisTemplate.keys(new StringBuffer(prefix).append(id).toString()));
}
long count = redisTemplate.delete(keys);
// 此处提示可自行删除
log.debug("--------------------------------------------");
log.debug("成功删除缓存:" + keys.toString());
log.debug("缓存删除数量:" + count + "个");
log.debug("--------------------------------------------");
}
}

121
common/src/main/java/com/canvas/web/utils/SpringContextHolder.java

@ -0,0 +1,121 @@
package com.canvas.web.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Component
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
private static final List<CallBack> CALL_BACKS = new ArrayList<>();
private static boolean addCallback = true;
public synchronized static void addCallBacks(CallBack callBack){
if (addCallback){
SpringContextHolder.CALL_BACKS.add(callBack);
}else {
log.warn("CallBack:{} 已无法添加!立即执行",callBack.getCallBackName());
callBack.executor();
}
}
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @param defaultValue 默认值
* @param requiredType 返回类型
* @return /
*/
public static <T> T getProperties(String property, T defaultValue, Class<T> requiredType) {
T result = defaultValue;
try {
result = getBean(Environment.class).getProperty(property, requiredType);
} catch (Exception ignored) {}
return result;
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @return /
*/
public static String getProperties(String property) {
return getProperties(property, null, String.class);
}
/**
* 获取SpringBoot 配置信息
*
* @param property 属性key
* @param requiredType 返回类型
* @return /
*/
public static <T> T getProperties(String property, Class<T> requiredType) {
return getProperties(property, null, requiredType);
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
}
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
private static void clearHolder() {
log.debug("清除SpringContextHolder中的ApplicationContext:"
+ applicationContext);
applicationContext = null;
}
@Override
public void destroy() {
SpringContextHolder.clearHolder();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringContextHolder.applicationContext != null) {
log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
}
SpringContextHolder.applicationContext = applicationContext;
if (addCallback) {
for (CallBack callBack : SpringContextHolder.CALL_BACKS) {
callBack.executor();
}
CALL_BACKS.clear();
}
SpringContextHolder.addCallback = false;
}
}

268
common/src/main/java/com/canvas/web/utils/StringUtils.java

@ -0,0 +1,268 @@
package com.canvas.web.utils;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.UserAgent;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbSearcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
public class StringUtils extends org.apache.commons.lang3.StringUtils{
private static final Logger log = LoggerFactory.getLogger(StringUtils.class);
private static boolean ipLocal = false;
private static File file = null;
private static DbConfig config;
private static final char SEPARATOR = '_';
private static final String UNKNOWN = "unknown";
static {
SpringContextHolder.addCallBacks(() -> {
StringUtils.ipLocal = SpringContextHolder.getProperties("ip.local-parsing", false, Boolean.class);
if (ipLocal) {
/*
* 此文件为独享 不必关闭
*/
String path = "ip2region/ip2region.db";
String name = "ip2region.db";
try {
config = new DbConfig();
file = FileUtil.inputStreamToFile(new ClassPathResource(path).getInputStream(), name);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
});
}
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld"
* toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toCamelCase(String s) {
if (s == null) {
return null;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == SEPARATOR) {
upperCase = true;
} else if (upperCase) {
sb.append(Character.toUpperCase(c));
upperCase = false;
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld"
* toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toCapitalizeCamelCase(String s) {
if (s == null) {
return null;
}
s = toCamelCase(s);
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld"
* toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
static String toUnderScoreCase(String s) {
if (s == null) {
return null;
}
StringBuilder sb = new StringBuilder();
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
boolean nextUpperCase = true;
if (i < (s.length() - 1)) {
nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
}
if ((i > 0) && Character.isUpperCase(c)) {
if (!upperCase || !nextUpperCase) {
sb.append(SEPARATOR);
}
upperCase = true;
} else {
upperCase = false;
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
/**
* 获取ip地址
*/
public static String getIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
String comma = ",";
String localhost = "127.0.0.1";
if (ip.contains(comma)) {
ip = ip.split(",")[0];
}
if (localhost.equals(ip)) {
// 获取本机真正的ip地址
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.error(e.getMessage(), e);
}
}
return ip;
}
/**
* 根据ip获取详细地址
*/
public static String getCityInfo(String ip) {
if (ipLocal) {
return getLocalCityInfo(ip);
} else {
return getHttpCityInfo(ip);
}
}
/**
* 根据ip获取详细地址
*/
public static String getHttpCityInfo(String ip) {
String api = String.format(ElAdminConstant.Url.IP_URL, ip);
JSONObject object = JSONUtil.parseObj(HttpUtil.get(api));
return object.get("addr", String.class);
}
/**
* 根据ip获取详细地址
*/
public static String getLocalCityInfo(String ip) {
try {
DataBlock dataBlock = new DbSearcher(config, file.getPath())
.binarySearch(ip);
String region = dataBlock.getRegion();
String address = region.replace("0|", "");
char symbol = '|';
if (address.charAt(address.length() - 1) == symbol) {
address = address.substring(0, address.length() - 1);
}
return address.equals(ElAdminConstant.REGION) ? "内网IP" : address;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return "";
}
public static String getBrowser(HttpServletRequest request) {
UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
Browser browser = userAgent.getBrowser();
return browser.getName();
}
/**
* 获得当天是周几
*/
public static String getWeekDay() {
String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
if (w < 0) {
w = 0;
}
return weekDays[w];
}
/**
* 获取当前机器的IP
*
* @return /
*/
public static String getLocalIp() {
try {
InetAddress candidateAddress = null;
// 遍历所有的网络接口
for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements();) {
NetworkInterface anInterface = interfaces.nextElement();
// 在所有的接口下再遍历IP
for (Enumeration<InetAddress> inetAddresses = anInterface.getInetAddresses(); inetAddresses.hasMoreElements();) {
InetAddress inetAddr = inetAddresses.nextElement();
// 排除loopback类型地址
if (!inetAddr.isLoopbackAddress()) {
if (inetAddr.isSiteLocalAddress()) {
// 如果是site-local地址就是它了
return inetAddr.getHostAddress();
} else if (candidateAddress == null) {
// site-local类型的地址未被发现先记录候选地址
candidateAddress = inetAddr;
}
}
}
}
if (candidateAddress != null) {
return candidateAddress.getHostAddress();
}
// 如果没有发现 non-loopback地址.只能用最次选的方案
InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
if (jdkSuppliedAddress == null) {
return "";
}
return jdkSuppliedAddress.getHostAddress();
} catch (Exception e) {
return "";
}
}
}

305
common/src/main/java/com/canvas/web/utils/UploadUtils.java

@ -0,0 +1,305 @@
package com.canvas.web.utils;
import com.canvas.web.config.UploadFileConfig;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
public class UploadUtils {
// 表单字段常量
public static final String FORM_FIELDS = "form_fields";
// 文件域常量
public static final String FILE_FIELDS = "file";
// 定义允许上传的文件扩展名
private Map<String, String> extMap = new HashMap<String, String>();
// 文件保存目录路径
private String uploadPath = UploadFileConfig.uploadFolder;
// 文件的目录名
private String dirName = "images";
// 上传临时路径
private static final String TEMP_PATH = "temp";
// 临时存相对路径
private String tempPath = uploadPath + TEMP_PATH;
// 单个文件最大上传大小(10M)
private long fileMaxSize = 1024 * 1024 * 10;
// 最大文件大小(100M)
private long maxSize = 1024 * 1024 * 100;
// 文件保存目录url
private String saveUrl;
// 文件最终的url包括文件名
private List<String> fileUrl = new ArrayList<>();
/**
* 构造函数
*/
public UploadUtils() {
// 其中images,flashs,medias,files,对应文件夹名称,对应dirName
// key文件夹名称
// value该文件夹内可以上传文件的后缀名
extMap.put("images", "gif,jpg,jpeg,png,bmp");
extMap.put("flashs", "swf,flv");
extMap.put("medias", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb");
extMap.put("files", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2");
}
/**
* 文件上传
*
* @param request
* @return
*/
@SuppressWarnings("unchecked")
public Map<String, Object> uploadFile(HttpServletRequest request, String name) {
// 验证文件并返回错误信息
String error = this.validateFields(request, name);
// 初始化表单元素
Map<String, Object> fieldsMap = new HashMap<String, Object>();
if (error.equals("")) {
fieldsMap = this.initFields(request);
}
List<FileItem> fiList = (List<FileItem>) fieldsMap.get(UploadUtils.FILE_FIELDS);
if (fiList != null) {
for (FileItem item : fiList) {
// 上传文件并返回错误信息
error = this.saveFile(item);
}
}
// 返回结果
Map<String, Object> result = new HashMap<>();
result.put("error", error);
result.put("image", this.fileUrl);
return result;
}
/**
* 上传验证并初始化目录
*
* @param request
* @return
*/
private String validateFields(HttpServletRequest request, String name) {
String errorInfo = "";
// 获取内容类型
String contentType = request.getContentType();
int contentLength = request.getContentLength();
// 初始化上传路径不存在则创建
File uploadDir = new File(uploadPath);
// 目录不存在则创建
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
if (contentType == null || !contentType.startsWith("multipart")) {
// TODO
System.out.println("请求不包含multipart/form-data流");
errorInfo = "请求不包含multipart/form-data流";
} else if (maxSize < contentLength) {
// TODO
System.out.println("上传文件大小超出文件最大大小");
errorInfo = "上传文件大小超出文件最大大小[" + maxSize + "]";
} else if (!ServletFileUpload.isMultipartContent(request)) {
// TODO
errorInfo = "请选择文件";
} else if (!uploadDir.isDirectory()) {
// TODO
errorInfo = "上传目录[" + uploadPath + "]不存在";
} else if (!uploadDir.canWrite()) {
// TODO
errorInfo = "上传目录[" + uploadPath + "]没有写权限";
} else if (!extMap.containsKey(dirName)) {
// TODO
errorInfo = "目录名不正确";
} else {
// 上传路径
uploadPath += dirName + "/" + name + "/";
// 保存目录Url
saveUrl = dirName + "/" + name + "/";
// 创建一级目录
File saveDirFile = new File(uploadPath);
if (!saveDirFile.exists()) {
saveDirFile.mkdirs();
}
// 创建二级目录(格式年月日)
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String ymd = sdf.format(new Date());
uploadPath += ymd + "/";
saveUrl += ymd + "/";
File dirFile = new File(uploadPath);
if (!dirFile.exists()) {
dirFile.mkdirs();
}
// 创建上传临时目录
File file = new File(tempPath);
if (!file.exists()) {
file.mkdirs();
}
}
return errorInfo;
}
/**
* 处理上传内容
*
* @return
*/
// @SuppressWarnings("unchecked")
private Map<String, Object> initFields(HttpServletRequest request) {
// 存储表单字段和非表单字段
Map<String, Object> map = new HashMap<String, Object>();
// 第一步判断request
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
// 第二步解析request
if (isMultipart) {
// 设置环境:创建一个DiskFileItemFactory工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
// 阀值,超过这个值才会写到临时目录,否则在内存中
factory.setSizeThreshold(1024 * 1024 * 10);
// 设置上传文件的临时目录
factory.setRepository(new File(tempPath));
// 核心操作类:创建一个文件上传解析器
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置文件名称编码(解决上传"文件名"的中文乱码)
upload.setHeaderEncoding("UTF-8");
// 限制单个文件上传大小
upload.setFileSizeMax(fileMaxSize);
// 限制总上传文件大小
upload.setSizeMax(maxSize);
// 使用ServletFileUpload解析器解析上传数据解析结果返回的是一个List<FileItem>集合每一个FileItem对应一个Form表单的输入项
List<FileItem> items = null;
try {
items = upload.parseRequest(request);
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 第3步处理uploaded items
if (items != null && items.size() > 0) {
Iterator<FileItem> iter = items.iterator();
// 文件域对象
List<FileItem> list = new ArrayList<FileItem>();
// 表单字段
Map<String, String> fields = new HashMap<String, String>();
while (iter.hasNext()) {
FileItem item = iter.next();
// 处理所有表单元素和文件域表单元素
if (item.isFormField()) {
// 如果fileitem中封装的是普通输入项的数据输出名
String name = item.getFieldName();// 普通输入项数据的名
String value = item.getString();
fields.put(name, value);
} else {
//如果fileitem中封装的是上传文件得到上传的文件名称
// 文件域表单元素
list.add(item);
}
}
map.put(FORM_FIELDS, fields);
map.put(FILE_FIELDS, list);
}
}
return map;
}
/**
* 保存文件
*
* @param item
* @return
*/
private String saveFile(FileItem item) {
String error = "";
String fileName = item.getName();
String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
if (item.getSize() > maxSize) { // 检查文件大小
// TODO
error = "上传文件大小超过限制";
} else if (!Arrays.<String>asList(extMap.get(dirName).split(",")).contains(fileExt)) {// 检查扩展名
error = "上传文件扩展名是不允许的扩展名。\n只允许" + extMap.get(dirName) + "格式。";
} else {
// 存储文件重命名
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
String newFileName = df.format(new Date()) + new Random().nextInt(1000) + "." + fileExt;
// 新增值文件数组
String filePath = saveUrl + newFileName;
fileUrl.add(filePath);
// 写入文件
try {
File uploadedFile = new File(uploadPath, newFileName);
item.write(uploadedFile);
} catch (IOException e) {
e.printStackTrace();
System.out.println("上传失败了!!!");
} catch (Exception e) {
e.printStackTrace();
}
}
return error;
}
/**
* *********************get/set方法*********************************
*/
public String getSaveUrl() {
return saveUrl;
}
public String getUploadPath() {
return uploadPath;
}
public long getMaxSize() {
return maxSize;
}
public void setMaxSize(long maxSize) {
this.maxSize = maxSize;
}
public Map<String, String> getExtMap() {
return extMap;
}
public void setExtMap(Map<String, String> extMap) {
this.extMap = extMap;
}
public String getDirName() {
return dirName;
}
public void setDirName(String dirName) {
this.dirName = dirName;
}
public String getTempPath() {
return tempPath;
}
public void setTempPath(String tempPath) {
this.tempPath = tempPath;
}
public List getFileUrl() {
return fileUrl;
}
public void setFileUrl(List fileUrl) {
this.fileUrl = fileUrl;
}
}

287
pom.xml

@ -4,18 +4,12 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/> <!-- 始终从仓库获取 -->
</parent>
<groupId>com.canvas.web</groupId>
<artifactId>yxk_canvasScreen</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>admin</module>
<module>common</module>
<module>generator</module>
<module>quartz</module>
@ -25,62 +19,233 @@
<name>多媒体后台管理系统</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.1</version>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<!-- 表示打包时跳过mvn test -->
<maven.test.skip>true</maven.test.skip>
<!--全局配置项目版本号-->
<version>1.0-SNAPSHOT</version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<log4jdbc.version>1.16</log4jdbc.version>
<swagger.version>2.9.2</swagger.version>
<fastjson.version>1.2.79</fastjson.version>
<druid.version>1.2.8</druid.version>
<commons-pool2.version>2.11.1</commons-pool2.version>
<mapstruct.version>1.4.2.Final</mapstruct.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 子模块依赖 -->
<dependency>
<groupId>com.javaweb</groupId>
<artifactId>javaweb-common</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>com.javaweb</groupId>
<artifactId>javaweb-generator</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>com.javaweb</groupId>
<artifactId>javaweb-system</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>com.javaweb</groupId>
<artifactId>javaweb-admin</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>com.javaweb</groupId>
<artifactId>javaweb-quartz</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>com.javaweb</groupId>
<artifactId>javaweb-queue</artifactId>
<version>${version}</version>
</dependency>
<!-- 第三方依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--Spring boot 核心-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--Spring boot Web容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Spring boot 测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--Spring boot 安全框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- spring boot 缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--Spring boot Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!-- <version>2.6.1</version>-->
</dependency>
<!--spring boot 集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${commons-pool2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!--监控sql日志-->
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
<version>${log4jdbc.version}</version>
</dependency>
<!-- Swagger UI 相关 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
<!--Mysql依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
<scope>runtime</scope>
</dependency>
<!-- postgresql 依赖包 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.19</version>
</dependency>
<!-- druid数据源驱动 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>1.7.2</version>
</dependency>
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<optional>true</optional>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.12.0</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--mapStruct依赖-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Java图形验证码 -->
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
<!--升级nashorn 版本解决JDK15 以上版本验证码报错问题-->
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.3</version>
</dependency>
<!-- 解析客户端操作系统、浏览器信息 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.21</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 打包时跳过测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

58
system/pom.xml

@ -13,40 +13,60 @@
<name>系统模块</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<jjwt.version>0.11.2</jjwt.version>
<!-- oshi监控需要指定jna版本, 问题详见 https://github.com/oshi/oshi/issues/1040 -->
<jna.version>5.6.0</jna.version>
</properties>
<!-- 依赖声明 -->
<dependencies>
<!-- 引入子模块依赖 -->
<dependency>
<groupId>com.canvas.web</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- jwt -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
</dependency>
<!-- Shiro 核心依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.version}</version>
</dependency>
<!-- Shiro-redis插件 -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.3.1</version>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt.version}</version>
</dependency>
<!-- 引入阿里数据库连接池 -->
<!-- linux的管理 -->
<dependency>
<groupId>ch.ethz.ganymed</groupId>
<artifactId>ganymed-ssh2</artifactId>
<version>build210</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<!-- 获取系统信息 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>5.3.6</version>
</dependency>
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
</dependencies>
</project>

45
system/src/main/java/AppRun.java

@ -0,0 +1,45 @@
import com.canvas.web.annotation.rest.AnonymousGetMapping;
import com.canvas.web.utils.SpringContextHolder;
import io.swagger.annotations.Api;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.bind.annotation.RestController;
@EnableAsync
@RestController
@Api(hidden = true)
@SpringBootApplication
@EnableTransactionManagement
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class AppRun {
public static void main(String[] args) {
SpringApplication.run(AppRun.class, args);
}
@Bean
public SpringContextHolder springContextHolder(){
return new SpringContextHolder();
}
public ServletWebServerFactory webServerFactory(){
TomcatServletWebServerFactory fa=new TomcatServletWebServerFactory();
fa.addConnectorCustomizers(connector -> connector.setProperty("relaxedQueryChars","[]{}"));
return fa;
}
/**
* 访问首页提示
* @return
*/
@AnonymousGetMapping("/")
public String index(){
return "Backend service started successfully";
}
}

24
system/src/main/java/modules/security/config/ConfigBeanConfiguration.java

@ -0,0 +1,24 @@
package modules.security.config;
import modules.security.config.bean.LoginProperties;
import modules.security.config.bean.SecurityProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConfigBeanConfiguration {
@Bean
@ConfigurationProperties(prefix="login",ignoreUnknownFields=true)
public LoginProperties loginProperties(){
return new LoginProperties();
}
@Bean
@ConfigurationProperties(prefix = "jwt",ignoreUnknownFields = true)
public SecurityProperties securityProperties(){
return new SecurityProperties();
}
}

31
system/src/main/java/modules/security/config/SpringSecurityConfig.java

@ -0,0 +1,31 @@
package modules.security.config;
import lombok.RequiredArgsConstructor;
import modules.security.security.JwtAccessDeniedHandler;
import modules.security.security.JwtAuthenticationEntryPoint;
import modules.security.security.SecurityProperties;
import modules.security.security.TokenProvider;
import modules.security.service.UserCacheClean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.filter.CorsFilter;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
private final TokenProvider tokenProvider;
private final CorsFilter corsFilter;
private final JwtAuthenticationEntryPoint authenticationErrorHandler;
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
private final ApplicationContext applicationContext;
private final SecurityProperties properties;
private final UserCacheClean userCacheClean;
}

48
system/src/main/java/modules/security/config/bean/LoginCode.java

@ -0,0 +1,48 @@
package modules.security.config.bean;
import lombok.Data;
@Data
public class LoginCode {
/**
* 验证码配置
*/
private LoginCodeEnum codeType;
/**
* 验证码有效期 分钟
*/
private Long expiration = 2L;
/**
* 验证码内容长度
*/
private int length = 2;
/**
* 验证码宽度
*/
private int width = 111;
/**
* 验证码高度
*/
private int height = 36;
/**
* 验证码字体
*/
private String fontName;
/**
* 字体大小
*/
private int fontSize = 25;
public LoginCodeEnum getCodeType() {
return codeType;
}
}

25
system/src/main/java/modules/security/config/bean/LoginCodeEnum.java

@ -0,0 +1,25 @@
package modules.security.config.bean;
//验证码配置枚举
public enum LoginCodeEnum {
/**
* 算术
*/
arithmetic,
/**
* 中文
*/
chinese,
/**
* 中文闪图
*/
chinese_gif,
/**
* 闪图
*/
gif,
spec
}

91
system/src/main/java/modules/security/config/bean/LoginProperties.java

@ -0,0 +1,91 @@
package modules.security.config.bean;
import com.canvas.web.exception.BaseException;
import com.canvas.web.exception.user.UserException;
import com.canvas.web.utils.StringUtils;
import com.wf.captcha.*;
import com.wf.captcha.base.Captcha;
import lombok.Data;
import java.awt.*;
import java.util.Objects;
@Data
public class LoginProperties {
/**
* 账号单用户 登录
*/
private boolean singleLogin = false;
private LoginCode loginCode;
/**
* 用户登录信息缓存
*/
private boolean cacheEnable;
public boolean isSingleLogin() {
return singleLogin;
}
public boolean isCacheEnable() {
return cacheEnable;
}
/**
* 获取验证码生产类
* @return
*/
public Captcha getCaptcha(){
if (Objects.isNull(loginCode)){
loginCode=new LoginCode();
if (Objects.isNull(loginCode.getCodeType())){
loginCode.setCodeType(LoginCodeEnum.arithmetic);
}
}
return switchCaptcha(loginCode);
}
/**
* 依据配置信息生产验证码
* @param loginCode
* @return
*/
private Captcha switchCaptcha(LoginCode loginCode){
Captcha captcha;
synchronized (this){
switch (loginCode.getCodeType()){
case arithmetic:
captcha=new ArithmeticCaptcha(loginCode.getWidth(),loginCode.getHeight());
//几位数运算默认是两位
captcha.setLen(loginCode.getLength());
break;
case chinese:
captcha=new ChineseCaptcha(loginCode.getWidth(),loginCode.getHeight());
captcha.setLen(loginCode.getLength());
case chinese_gif:
captcha=new ChineseGifCaptcha(loginCode.getWidth(),loginCode.getHeight());
captcha.setLen(loginCode.getLength());
break;
case gif:
captcha=new GifCaptcha(loginCode.getWidth(),loginCode.getHeight());
captcha.setLen(loginCode.getLength());
case spec:
captcha=new SpecCaptcha(loginCode.getWidth(),loginCode.getHeight());
captcha.setLen(loginCode.getLength());
break;
default:
throw new BaseException("验证码配置信息错误!正确配置查看 LoginCodeEnum");
}
}
if (StringUtils.isNotBlank(loginCode.getFontName())){
captcha.setFont(new Font(loginCode.getFontName(), Font.PLAIN, loginCode.getFontSize()));
}
return captcha;
}
}

52
system/src/main/java/modules/security/config/bean/SecurityProperties.java

@ -0,0 +1,52 @@
package modules.security.config.bean;
import lombok.Data;
@Data
public class SecurityProperties {
/**
* Request Headers:Authorization
*/
private String header;
/**
* 令牌前缀最后留个空格 Bearer
*/
private String tokenStartWith;
/**
* 必须使用最少88位的Base64对该令牌进行编码
*/
private String base64Secret;
/**
* 令牌过期时间此处单位/毫秒
*/
private Long tokenValidityInSeconds;
/**
* 在线用户 key,根据 key 查询 redis 中在线用户的数据
*/
private String onlineKey;
/**
* 验证码key
*/
private String codeKey;
/**
* token 续期检查
*/
private Long detect;
/**
* 续期时间
*/
private Long renew;
public String getTokenStartWith(){
return tokenStartWith+" ";
}
}

19
system/src/main/java/modules/security/security/JwtAccessDeniedHandler.java

@ -0,0 +1,19 @@
package modules.security.security;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
//当前用户在没有权限的情况下访问受保护的REST资源时将调用此方法发送403 Forbidden响应
response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());
}
}

19
system/src/main/java/modules/security/security/JwtAuthenticationEntryPoint.java

@ -0,0 +1,19 @@
package modules.security.security;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException == null ? "Unauthorized" : authException.getMessage());
}
}

51
system/src/main/java/modules/security/security/SecurityProperties.java

@ -0,0 +1,51 @@
package modules.security.security;
import lombok.Data;
@Data
public class SecurityProperties {
/**
* Request Headers:Authorization
*/
private String header;
/**
* 令牌前缀最后留个空格 Bearer
*/
private String tokenStartWith;
/**
* 必须使用最少88位的Base64对该令牌进行编码
*/
private String base64Secret;
/**
* 令牌过期时间此处单位/毫秒
*/
private Long tokenValidityInSeconds;
/**
* 在线用户 key,根据 key 查询 redis 中在线用户的数据
*/
private String onlineKey;
/**
* 验证码key
*/
private String codeKey;
/**
* token 续期检查
*/
private Long detect;
/**
* 续期时间
*/
private Long renew;
public String getTokenStartWith(){
return tokenStartWith+" ";
}
}

16
system/src/main/java/modules/security/security/TokenConfigurer.java

@ -0,0 +1,16 @@
package modules.security.security;
import lombok.RequiredArgsConstructor;
import modules.security.service.OnlineUserService;
import modules.security.service.UserCacheClean;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
@RequiredArgsConstructor
public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final TokenProvider tokenProvider;
private final SecurityProperties properties;
private final OnlineUserService onlineUserService;
private final UserCacheClean userCacheClean;
}

107
system/src/main/java/modules/security/security/TokenProvider.java

@ -0,0 +1,107 @@
package modules.security.security;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.canvas.web.utils.RedisUtils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class TokenProvider implements InitializingBean {
private final SecurityProperties properties;
private final RedisUtils redisUtils;
public static final String AUTHORITIES_KEY="auth";
private JwtParser jwtParser;
private JwtBuilder jwtBuilder;
public TokenProvider(SecurityProperties properties,RedisUtils redisUtils){
this.properties=properties;
this.redisUtils=redisUtils;
}
@Override
public void afterPropertiesSet() {
byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret());
Key key = Keys.hmacShaKeyFor(keyBytes);
jwtParser = Jwts.parserBuilder()
.setSigningKey(key)
.build();
jwtBuilder = Jwts.builder()
.signWith(key, SignatureAlgorithm.HS512);
}
/**
* 创建Token 设置永不过期
* Token 的时间有效性转到Redis 维护
* @param authentication
* @return
*/
public String createToken(Authentication authentication) {
return jwtBuilder
// 加入ID确保生成的 Token 都不一致
.setId(IdUtil.simpleUUID())
.setSubject(authentication.getName())
.compact();
}
/**
* 依据Token 获取鉴权信息
* @param token
* @return
*/
Authentication getAuthentication(String token){
Claims claims=getClaims(token);
User principal = new User(claims.getSubject(), "******", new ArrayList<>());
return new UsernamePasswordAuthenticationToken(principal, token, new ArrayList<>());
}
public Claims getClaims(String token) {
return jwtParser
.parseClaimsJws(token)
.getBody();
}
/**
* @param token 需要检查的token
*/
public void checkRenewal(String token) {
// 判断是否续期token,计算token的过期时间
long time = redisUtils.getExpire(properties.getOnlineKey() + token) * 1000;
Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time);
// 判断当前时间与过期时间的时间差
long differ = expireDate.getTime() - System.currentTimeMillis();
// 如果在续期检查的范围内则续期
if (differ <= properties.getDetect()) {
long renew = time + properties.getRenew();
redisUtils.expire(properties.getOnlineKey() + token, renew, TimeUnit.MILLISECONDS);
}
}
public String getToken(HttpServletRequest request) {
final String requestHeader = request.getHeader(properties.getHeader());
if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {
return requestHeader.substring(7);
}
return null;
}
}

19
system/src/main/java/modules/security/service/OnlineUserService.java

@ -0,0 +1,19 @@
package modules.security.service;
import com.canvas.web.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import modules.security.config.bean.SecurityProperties;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class OnlineUserService {
private final SecurityProperties properties;
private final RedisUtils redisUtils;
public OnlineUserService(SecurityProperties properties, RedisUtils redisUtils) {
this.properties = properties;
this.redisUtils = redisUtils;
}
}

31
system/src/main/java/modules/security/service/UserCacheClean.java

@ -0,0 +1,31 @@
package modules.security.service;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
//用于清理用户登录信息缓存
@Component
public class UserCacheClean {
/**
* 清理特定用户缓存信息
* 用户信息变更时
*
* @param userName
*/
public void cleanUserCache(String userName) {
if (StringUtils.isNotEmpty(userName)) {
UserDetailsServiceImpl.userDtoCache.remove(userName);
}
}
/**
* 清理所有用户的缓存信息
* ,如发生角色授权信息变化可以简便的全部失效缓存
*/
public void cleanAll() {
UserDetailsServiceImpl.userDtoCache.clear();
}
}

74
system/src/main/java/modules/security/service/UserDetailsServiceImpl.java

@ -0,0 +1,74 @@
package modules.security.service;
import com.canvas.web.exception.BaseException;
import lombok.RequiredArgsConstructor;
import modules.security.config.bean.LoginProperties;
import modules.security.service.dto.JwtUserDto;
import modules.system.service.DataService;
import modules.system.service.RoleService;
import modules.system.service.UserService;
import modules.system.service.dto.UserDto;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.persistence.EntityNotFoundException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@RequiredArgsConstructor
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserService userService;
private final RoleService roleService;
private final DataService dataService;
private final LoginProperties loginProperties;
public void setEnableCache(boolean enableCache) {
this.loginProperties.setCacheEnable(enableCache);
}
/**
* 用户信息缓存
*
*
*/
static Map<String, JwtUserDto> userDtoCache = new ConcurrentHashMap<>();
@Override
public JwtUserDto loadUserByUsername(String username) {
boolean searchDb = true;
JwtUserDto jwtUserDto = null;
if (loginProperties.isCacheEnable() && userDtoCache.containsKey(username)) {
jwtUserDto = userDtoCache.get(username);
searchDb = false;
}
if (searchDb) {
UserDto user;
try {
user = userService.findByName(username);
} catch (EntityNotFoundException e) {
// SpringSecurity会自动转换UsernameNotFoundException为BadCredentialsException
throw new UsernameNotFoundException("", e);
}
if (user == null) {
throw new UsernameNotFoundException("");
} else {
if (!user.getEnabled()) {
throw new BaseException("账号未激活!");
}
jwtUserDto = new JwtUserDto(
user,
dataService.getDeptIds(user),
roleService.mapToGrantedAuthorities(user)
);
userDtoCache.put(username, jwtUserDto);
}
}
return jwtUserDto;
}
}

22
system/src/main/java/modules/security/service/dto/AuthUserDto.java

@ -0,0 +1,22 @@
package modules.security.service.dto;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
@Getter
@Setter
public class AuthUserDto {
@NotBlank
private String username;
@NotBlank
private String password;
private String code;
private String uuid = "";
}

65
system/src/main/java/modules/security/service/dto/JwtUserDto.java

@ -0,0 +1,65 @@
package modules.security.service.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Getter;
import modules.system.service.dto.UserDto;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
public class JwtUserDto implements UserDetails {
private final UserDto user;
private final List<Long> dataScopes;
@JsonIgnore
private final List<GrantedAuthority> authorities;
public Set<String> getRoles() {
return authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
}
@Override
@JsonIgnore
public String getPassword() {
return user.getPassword();
}
@Override
@JsonIgnore
public String getUsername() {
return user.getUsername();
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
@JsonIgnore
public boolean isEnabled() {
return user.getEnabled();
}
}

15
system/src/main/java/modules/system/service/DataService.java

@ -0,0 +1,15 @@
package modules.system.service;
import modules.system.service.dto.UserDto;
import java.util.List;
public interface DataService {
/**
* 获取数据权限
* @param user /
* @return /
*/
List<Long> getDeptIds(UserDto user);
}

16
system/src/main/java/modules/system/service/RoleService.java

@ -0,0 +1,16 @@
package modules.system.service;
import modules.system.service.dto.UserDto;
import org.springframework.security.core.GrantedAuthority;
import java.util.List;
public interface RoleService {
/**
* 获取用户权限信息
* @param user 用户信息
* @return 权限信息
*/
List<GrantedAuthority> mapToGrantedAuthorities(UserDto user);
}

98
system/src/main/java/modules/system/service/UserService.java

@ -0,0 +1,98 @@
package modules.system.service;
import modules.system.service.dto.UserDto;
import modules.system.service.dto.UserQueryCriteria;
import org.springframework.data.domain.Pageable;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
public interface UserService {
/**
* 根据ID查询
* @param id ID
* @return /
*/
UserDto findById(long id);
/**
* 新增用户
* @param resources /
*/
// void create(User resources);
/**
* 编辑用户
* @param resources /
*/
// void update(User resources);
/**
* 删除用户
* @param ids /
*/
void delete(Set<Long> ids);
/**
* 根据用户名查询
* @param userName /
* @return /
*/
UserDto findByName(String userName);
/**
* 修改密码
* @param username 用户名
* @param encryptPassword 密码
*/
void updatePass(String username, String encryptPassword);
/**
* 修改头像
* @param file 文件
* @return /
*/
Map<String, String> updateAvatar(MultipartFile file);
/**
* 修改邮箱
* @param username 用户名
* @param email 邮箱
*/
void updateEmail(String username, String email);
/**
* 查询全部
* @param criteria 条件
* @param pageable 分页参数
* @return /
*/
Object queryAll(UserQueryCriteria criteria, Pageable pageable);
/**
* 查询全部不分页
* @param criteria 条件
* @return /
*/
List<UserDto> queryAll(UserQueryCriteria criteria);
/**
* 导出数据
* @param queryAll 待导出的数据
* @param response /
* @throws IOException /
*/
void download(List<UserDto> queryAll, HttpServletResponse response) throws IOException;
/**
* 用户自助修改资料
* @param resources /
*/
// void updateCenter(User resources);
}

76
system/src/main/java/modules/system/service/dto/MenuDto.java

@ -0,0 +1,76 @@
package modules.system.service.dto;
import com.canvas.web.base.BaseDTO;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
@Getter
@Setter
public class MenuDto extends BaseDTO implements Serializable {
private Long id;
private List<MenuDto> children;
private Integer type;
private String permission;
private String title;
private Integer menuSort;
private String path;
private String component;
private Long pid;
private Integer subCount;
private Boolean iFrame;
private Boolean cache;
private Boolean hidden;
private String componentName;
private String icon;
private Integer showPosition;
public Boolean getHasChildren() {
return subCount > 0;
}
public Boolean getLeaf() {
return subCount <= 0;
}
public String getLabel() {
return title;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MenuDto menuDto = (MenuDto) o;
return Objects.equals(id, menuDto.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}

45
system/src/main/java/modules/system/service/dto/RoleDto.java

@ -0,0 +1,45 @@
package modules.system.service.dto;
import com.canvas.web.base.BaseDTO;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Objects;
import java.util.Set;
@Getter
@Setter
public class RoleDto extends BaseDTO implements Serializable {
private Long id;
private Set<MenuDto> menus;
// private Set<DeptDto> depts;
private String name;
private String dataScope;
private Integer level;
private String description;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RoleDto roleDto = (RoleDto) o;
return Objects.equals(id, roleDto.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}

16
system/src/main/java/modules/system/service/dto/RoleSmallDto.java

@ -0,0 +1,16 @@
package modules.system.service.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class RoleSmallDto implements Serializable {
private Long id;
private String name;
private Integer level;
private String dataScope;
}

50
system/src/main/java/modules/system/service/dto/UserDto.java

@ -0,0 +1,50 @@
package modules.system.service.dto;
import com.canvas.web.base.BaseDTO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
import java.util.Set;
@Getter
@Setter
public class UserDto extends BaseDTO implements Serializable {
private Long id;
private Set<RoleSmallDto> roles;
//private Set<JobSmallDto> jobs;
// private DeptSmallDto dept;
private Long deptId;
private String username;
private String nickName;
private String email;
private String phone;
private String gender;
private String avatarName;
private String avatarPath;
@JsonIgnore
private String password;
private Boolean enabled;
@JsonIgnore
private Boolean isAdmin = false;
private Date pwdResetTime;
}

32
system/src/main/java/modules/system/service/dto/UserQueryCriteria.java

@ -0,0 +1,32 @@
package modules.system.service.dto;
import com.canvas.web.annotation.Query;
import lombok.Data;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Data
public class UserQueryCriteria implements Serializable {
@Query
private Long id;
@Query(propName = "id", type = Query.Type.IN, joinName = "dept")
private Set<Long> deptIds = new HashSet<>();
@Query(blurry = "email,username,nickName")
private String blurry;
@Query
private Boolean enabled;
private Long deptId;
@Query(type = Query.Type.BETWEEN)
private List<Timestamp> createTime;
}

115
system/src/main/resources/config/application-dev.yml

@ -0,0 +1,115 @@
spring:
datasource:
druid:
db-type: com.alibaba.druid.pool.DruidDataSource
driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
url: jdbc:log4jdbc:mysql://${DB_HOST:192.168.99.207}:${DB_PORT:3306}/${DB_NAME:yxkadmin}?serverTimezone=Asia/Shanghai&characterEncoding=utf-8&useSSL=false
username: ${DB_USER:root}
password: ${DB_PWD:ftzn83560792}
# 初始连接数
initial-size: 5
# 最小连接数
min-idle: 10
# 最大连接数
max-active: 20
# 获取连接超时时间
max-wait: 5000
# 连接有效性检测时间
time-between-eviction-runs-millis: 60000
# 连接在池中最小生存的时间
min-evictable-idle-time-millis: 300000
# 连接在池中最大生存的时间
max-evictable-idle-time-millis: 900000
test-while-idle: true
test-on-borrow: false
test-on-return: false
useGlobalDataSourceStat: true
# 检测连接是否有效
validation-query: select 1
# 配置监控统计
webStatFilter:
enabled: true
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: false
login-username: admin
login-password: 123456
filter:
stat:
enabled: true
# 记录慢SQL
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# 登录相关配置
login:
# 登录缓存
cache-enable: true
# 是否限制单用户登录
single-login: false
# 验证码
login-code:
# 验证码类型配置 查看 LoginProperties 类
code-type: arithmetic
# 登录图形验证码有效时间/分钟
expiration: 2
# 验证码高度
width: 111
# 验证码宽度
heigth: 36
# 内容长度
length: 2
# 字体名称,为空则使用默认字体
font-name:
# 字体大小
font-size: 25
#jwt
jwt:
header: Authorization
# 令牌前缀
token-start-with: Bearer
# 必须使用最少88位的Base64对该令牌进行编码
base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=
# 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
token-validity-in-seconds: 14400000
# 在线用户key
online-key: online-token-
# 验证码
code-key: code-key-
# token 续期检查时间范围(默认30分钟,单位毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期
detect: 1800000
# 续期时间范围,默认1小时,单位毫秒
renew: 3600000
#是否允许生成代码,生产环境设置为false
generator:
enabled: true
#是否开启 swagger-ui
swagger:
enabled: true
# IP 本地解析
ip:
local-parsing: true
# 文件存储路径
file:
mac:
path: ~/file/
avatar: ~/avatar/
linux:
path: /home/canvasscreen/file/
avatar: /home/canvasscreen/avatar/
windows:
path: F:\code\canvasscreen\file\
avatar: F:\code\canvasscreen\avatar\
# 文件大小 /M
maxSize: 100
avatarMaxSize: 5

0
admin/src/main/resources/application-prod.yml → system/src/main/resources/config/application-prod.yml

60
system/src/main/resources/config/application.yml

@ -0,0 +1,60 @@
server:
port: 7000
spring:
freemarker:
check-template-location: false
profiles:
active: dev
jackson:
time-zone: GMT+8
data:
redis:
repositories:
enabled: false
main:
allow-bean-definition-overriding: true
#配置 Jpa
jpa:
properties:
hibernate:
ddl-auto: none
#dialect: org.hibernate.dialect.MySQL5InnoDBDialect
# naming:
# physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
open-in-view: true
redis:
#数据库索引
database: ${REDIS_DB:0}
host: ${REDIS_HOST:192.168.99.207}
port: ${REDIS_PORT:6379}
password: ${REDIS_PWD:ftzn83560792}
#连接超时时间
timeout: 5000
task:
pool:
# 核心线程池大小
core-pool-size: 10
# 最大线程数
max-pool-size: 30
# 活跃时间
keep-alive-seconds: 60
# 队列容量
queue-capacity: 50
#七牛云
qiniu:
# 文件大小 /M
max-size: 15
#邮箱验证码有效时间/秒
code:
expiration: 300
#密码加密传输,前端公钥加密,后端私钥解密
rsa:
private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==
Loading…
Cancel
Save