From 2d407e0e794487be81ab54506870262619a55804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8A=9B?= Date: Tue, 15 Feb 2022 11:41:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/canvas/web/base/BaseEntity.java | 67 +++++++ .../com/canvas/web/config/FileProperties.java | 43 +++++ .../canvas/web/enums/RequestMethodEnum.java | 52 +++++ .../com/canvas/web/utils/EncryptUtils.java | 79 ++++++++ .../java/com/canvas/web/utils/PageUtil.java | 46 +++++ system/pom.xml | 7 + .../canvas/web/config/ConfigurerAdapter.java | 47 +++++ .../canvas/web/config/WebSocketConfig.java | 14 ++ .../config/thread/AsyncTaskExecutePool.java | 51 +++++ .../config/thread/AsyncTaskProperties.java | 20 ++ .../web/config/thread/TheadFactoryName.java | 44 +++++ .../modules/config/SpringSecurityConfig.java | 32 ---- .../config/bean/SecurityProperties.java | 52 ----- .../controller/AuthorizationController.java | 12 +- .../modules/controller/OnlineController.java | 2 +- .../config/ConfigBeanConfiguration.java | 7 +- .../security/config/SpringSecurityConfig.java | 178 ++++++++++++++++++ .../{ => security}/config/bean/LoginCode.java | 3 +- .../config/bean/LoginCodeEnum.java | 3 +- .../config/bean/LoginProperties.java | 2 +- .../{ => config/bean}/SecurityProperties.java | 2 +- .../JwtAccessDeniedHandler.java | 2 +- .../JwtAuthenticationEntryPoint.java | 2 +- .../{ => security}/TokenConfigurer.java | 7 +- .../security/security/TokenFilter.java | 91 +++++++++ .../{ => security}/TokenProvider.java | 3 +- .../security/service/OnlineUserService.java | 168 +++++++++++++++++ .../service/UserCacheClean.java | 2 +- .../service/UserDetailsServiceImpl.java | 6 +- .../service/dto/AuthUserDto.java | 2 +- .../service/dto/JwtUserDto.java | 2 +- .../service/dto/OnlineUserDto.java | 2 +- .../modules/service/OnlineUserService.java | 43 ----- .../web/modules/system/domain/Menu.java | 89 +++++++++ .../canvas/web/modules/system/domain/Org.java | 8 + .../web/modules/system/domain/Role.java | 79 ++++++++ .../web/modules/system/domain/User.java | 104 ++++++++++ .../system/repository/MenuRepository.java | 4 + .../system/repository/OrgRepository.java | 4 + .../system/repository/RoleRepository.java | 4 + .../system/repository/UserRepository.java | 4 + 41 files changed, 1233 insertions(+), 156 deletions(-) create mode 100644 common/src/main/java/com/canvas/web/base/BaseEntity.java create mode 100644 common/src/main/java/com/canvas/web/config/FileProperties.java create mode 100644 common/src/main/java/com/canvas/web/enums/RequestMethodEnum.java create mode 100644 common/src/main/java/com/canvas/web/utils/EncryptUtils.java create mode 100644 common/src/main/java/com/canvas/web/utils/PageUtil.java create mode 100644 system/src/main/java/com/canvas/web/config/ConfigurerAdapter.java create mode 100644 system/src/main/java/com/canvas/web/config/WebSocketConfig.java create mode 100644 system/src/main/java/com/canvas/web/config/thread/AsyncTaskExecutePool.java create mode 100644 system/src/main/java/com/canvas/web/config/thread/AsyncTaskProperties.java create mode 100644 system/src/main/java/com/canvas/web/config/thread/TheadFactoryName.java delete mode 100644 system/src/main/java/com/canvas/web/modules/config/SpringSecurityConfig.java delete mode 100644 system/src/main/java/com/canvas/web/modules/config/bean/SecurityProperties.java rename system/src/main/java/com/canvas/web/modules/{ => security}/config/ConfigBeanConfiguration.java (76%) create mode 100644 system/src/main/java/com/canvas/web/modules/security/config/SpringSecurityConfig.java rename system/src/main/java/com/canvas/web/modules/{ => security}/config/bean/LoginCode.java (92%) rename system/src/main/java/com/canvas/web/modules/{ => security}/config/bean/LoginCodeEnum.java (76%) rename system/src/main/java/com/canvas/web/modules/{ => security}/config/bean/LoginProperties.java (98%) rename system/src/main/java/com/canvas/web/modules/security/{ => config/bean}/SecurityProperties.java (94%) rename system/src/main/java/com/canvas/web/modules/security/{ => security}/JwtAccessDeniedHandler.java (94%) rename system/src/main/java/com/canvas/web/modules/security/{ => security}/JwtAuthenticationEntryPoint.java (93%) rename system/src/main/java/com/canvas/web/modules/security/{ => security}/TokenConfigurer.java (70%) create mode 100644 system/src/main/java/com/canvas/web/modules/security/security/TokenFilter.java rename system/src/main/java/com/canvas/web/modules/security/{ => security}/TokenProvider.java (96%) create mode 100644 system/src/main/java/com/canvas/web/modules/security/service/OnlineUserService.java rename system/src/main/java/com/canvas/web/modules/{ => security}/service/UserCacheClean.java (93%) rename system/src/main/java/com/canvas/web/modules/{ => security}/service/UserDetailsServiceImpl.java (93%) rename system/src/main/java/com/canvas/web/modules/{ => security}/service/dto/AuthUserDto.java (84%) rename system/src/main/java/com/canvas/web/modules/{ => security}/service/dto/JwtUserDto.java (96%) rename system/src/main/java/com/canvas/web/modules/{ => security}/service/dto/OnlineUserDto.java (92%) delete mode 100644 system/src/main/java/com/canvas/web/modules/service/OnlineUserService.java create mode 100644 system/src/main/java/com/canvas/web/modules/system/domain/Menu.java create mode 100644 system/src/main/java/com/canvas/web/modules/system/domain/Org.java create mode 100644 system/src/main/java/com/canvas/web/modules/system/domain/Role.java create mode 100644 system/src/main/java/com/canvas/web/modules/system/domain/User.java create mode 100644 system/src/main/java/com/canvas/web/modules/system/repository/MenuRepository.java create mode 100644 system/src/main/java/com/canvas/web/modules/system/repository/OrgRepository.java create mode 100644 system/src/main/java/com/canvas/web/modules/system/repository/RoleRepository.java create mode 100644 system/src/main/java/com/canvas/web/modules/system/repository/UserRepository.java diff --git a/common/src/main/java/com/canvas/web/base/BaseEntity.java b/common/src/main/java/com/canvas/web/base/BaseEntity.java new file mode 100644 index 0000000..95c584a --- /dev/null +++ b/common/src/main/java/com/canvas/web/base/BaseEntity.java @@ -0,0 +1,67 @@ +package com.canvas.web.base; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.sql.Timestamp; + + +@Getter +@Setter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public class BaseEntity implements Serializable { + + @CreatedBy + @Column(name = "create_by", updatable = false) + @ApiModelProperty(value = "创建人", hidden = true) + private String createBy; + + @LastModifiedBy + @Column(name = "update_by") + @ApiModelProperty(value = "更新人", hidden = true) + private String updatedBy; + + @CreationTimestamp + @Column(name = "create_time", updatable = false) + @ApiModelProperty(value = "创建时间", hidden = true) + private Timestamp createTime; + + @UpdateTimestamp + @Column(name = "update_time") + @ApiModelProperty(value = "更新时间", hidden = true) + private Timestamp updateTime; + + /* 分组校验 */ + public @interface Create {} + + /* 分组校验 */ + public @interface Update {} + + @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(); + } +} diff --git a/common/src/main/java/com/canvas/web/config/FileProperties.java b/common/src/main/java/com/canvas/web/config/FileProperties.java new file mode 100644 index 0000000..174f856 --- /dev/null +++ b/common/src/main/java/com/canvas/web/config/FileProperties.java @@ -0,0 +1,43 @@ +package com.canvas.web.config; + + +import com.canvas.web.utils.ElAdminConstant; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "file") +public class FileProperties { + + /** 文件大小限制 */ + private Long maxSize; + + /** 头像大小限制 */ + private Long avatarMaxSize; + + private ElPath mac; + + private ElPath linux; + + private ElPath windows; + + public ElPath getPath(){ + String os = System.getProperty("os.name"); + if(os.toLowerCase().startsWith(ElAdminConstant.WIN)) { + return windows; + } else if(os.toLowerCase().startsWith(ElAdminConstant.MAC)){ + return mac; + } + return linux; + } + + @Data + public static class ElPath{ + + private String path; + + private String avatar; + } +} diff --git a/common/src/main/java/com/canvas/web/enums/RequestMethodEnum.java b/common/src/main/java/com/canvas/web/enums/RequestMethodEnum.java new file mode 100644 index 0000000..8c77476 --- /dev/null +++ b/common/src/main/java/com/canvas/web/enums/RequestMethodEnum.java @@ -0,0 +1,52 @@ +package com.canvas.web.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum RequestMethodEnum { + /** + * 搜寻 @AnonymousGetMapping + */ + GET("GET"), + + /** + * 搜寻 @AnonymousPostMapping + */ + POST("POST"), + + /** + * 搜寻 @AnonymousPutMapping + */ + PUT("PUT"), + + /** + * 搜寻 @AnonymousPatchMapping + */ + PATCH("PATCH"), + + /** + * 搜寻 @AnonymousDeleteMapping + */ + DELETE("DELETE"), + + /** + * 否则就是所有 Request 接口都放行 + */ + ALL("All"); + + /** + * Request 类型 + */ + private final String type; + + public static RequestMethodEnum find(String type) { + for (RequestMethodEnum value : RequestMethodEnum.values()) { + if (type.equals(value.getType())) { + return value; + } + } + return ALL; + } +} diff --git a/common/src/main/java/com/canvas/web/utils/EncryptUtils.java b/common/src/main/java/com/canvas/web/utils/EncryptUtils.java new file mode 100644 index 0000000..7f37e57 --- /dev/null +++ b/common/src/main/java/com/canvas/web/utils/EncryptUtils.java @@ -0,0 +1,79 @@ +package com.canvas.web.utils; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.IvParameterSpec; +import java.nio.charset.StandardCharsets; + +public class EncryptUtils { + + private static final String STR_PARAM = "Passw0rd"; + + private static Cipher cipher; + + private static final IvParameterSpec IV = new IvParameterSpec(STR_PARAM.getBytes(StandardCharsets.UTF_8)); + + private static DESKeySpec getDesKeySpec(String source) throws Exception { + if (source == null || source.length() == 0){ + return null; + } + cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); + String strKey = "Passw0rd"; + return new DESKeySpec(strKey.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 对称加密 + */ + public static String desEncrypt(String source) throws Exception { + DESKeySpec desKeySpec = getDesKeySpec(source); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = keyFactory.generateSecret(desKeySpec); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, IV); + return byte2hex( + cipher.doFinal(source.getBytes(StandardCharsets.UTF_8))).toUpperCase(); + } + + /** + * 对称解密 + */ + public static String desDecrypt(String source) throws Exception { + byte[] src = hex2byte(source.getBytes(StandardCharsets.UTF_8)); + DESKeySpec desKeySpec = getDesKeySpec(source); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = keyFactory.generateSecret(desKeySpec); + cipher.init(Cipher.DECRYPT_MODE, secretKey, IV); + byte[] retByte = cipher.doFinal(src); + return new String(retByte); + } + + private static String byte2hex(byte[] inStr) { + String stmp; + StringBuilder out = new StringBuilder(inStr.length * 2); + for (byte b : inStr) { + stmp = Integer.toHexString(b & 0xFF); + if (stmp.length() == 1) { + // 如果是0至F的单位字符串,则添加0 + out.append("0").append(stmp); + } else { + out.append(stmp); + } + } + return out.toString(); + } + + private static byte[] hex2byte(byte[] b) { + int size = 2; + if ((b.length % size) != 0){ + throw new IllegalArgumentException("长度不是偶数"); + } + byte[] b2 = new byte[b.length / 2]; + for (int n = 0; n < b.length; n += size) { + String item = new String(b, n, 2); + b2[n / 2] = (byte) Integer.parseInt(item, 16); + } + return b2; + } +} diff --git a/common/src/main/java/com/canvas/web/utils/PageUtil.java b/common/src/main/java/com/canvas/web/utils/PageUtil.java new file mode 100644 index 0000000..cab6a2e --- /dev/null +++ b/common/src/main/java/com/canvas/web/utils/PageUtil.java @@ -0,0 +1,46 @@ +package com.canvas.web.utils; + +import org.springframework.data.domain.Page; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class PageUtil extends cn.hutool.core.util.PageUtil{ + + /** + * List 分页 + */ + public static List toPage(int page, int size , List list) { + int fromIndex = page * size; + int toIndex = page * size + size; + if(fromIndex > list.size()){ + return new ArrayList(); + } else if(toIndex >= list.size()) { + return list.subList(fromIndex,list.size()); + } else { + return list.subList(fromIndex,toIndex); + } + } + + /** + * Page 数据处理,预防redis反序列化报错 + */ + public static Map toPage(Page page) { + Map map = new LinkedHashMap<>(2); + map.put("content",page.getContent()); + map.put("totalElements",page.getTotalElements()); + return map; + } + + /** + * 自定义分页 + */ + public static Map toPage(Object object, Object totalElements) { + Map map = new LinkedHashMap<>(2); + map.put("content",object); + map.put("totalElements",totalElements); + return map; + } +} diff --git a/system/pom.xml b/system/pom.xml index c06eec0..d3d9035 100644 --- a/system/pom.xml +++ b/system/pom.xml @@ -25,6 +25,13 @@ common 1.0-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-websocket + + io.jsonwebtoken diff --git a/system/src/main/java/com/canvas/web/config/ConfigurerAdapter.java b/system/src/main/java/com/canvas/web/config/ConfigurerAdapter.java new file mode 100644 index 0000000..8fe3bb5 --- /dev/null +++ b/system/src/main/java/com/canvas/web/config/ConfigurerAdapter.java @@ -0,0 +1,47 @@ +package com.canvas.web.config; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +@EnableWebMvc +public class ConfigurerAdapter implements WebMvcConfigurer { + + /** 文件配置 */ + private final FileProperties properties; + + public ConfigurerAdapter(FileProperties properties) { + this.properties = properties; + } + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOriginPattern("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + + + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + FileProperties.ElPath path = properties.getPath(); + String avatarUtl = "file:" + path.getAvatar().replace("\\","/"); + String pathUtl = "file:" + path.getPath().replace("\\","/"); + registry.addResourceHandler("/avatar/**").addResourceLocations(avatarUtl).setCachePeriod(0); + registry.addResourceHandler("/file/**").addResourceLocations(pathUtl).setCachePeriod(0); + registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0); + } +} diff --git a/system/src/main/java/com/canvas/web/config/WebSocketConfig.java b/system/src/main/java/com/canvas/web/config/WebSocketConfig.java new file mode 100644 index 0000000..1b2eeb1 --- /dev/null +++ b/system/src/main/java/com/canvas/web/config/WebSocketConfig.java @@ -0,0 +1,14 @@ +package com.canvas.web.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration +public class WebSocketConfig { + + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } +} diff --git a/system/src/main/java/com/canvas/web/config/thread/AsyncTaskExecutePool.java b/system/src/main/java/com/canvas/web/config/thread/AsyncTaskExecutePool.java new file mode 100644 index 0000000..f4bf936 --- /dev/null +++ b/system/src/main/java/com/canvas/web/config/thread/AsyncTaskExecutePool.java @@ -0,0 +1,51 @@ +package com.canvas.web.config.thread; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + + +@Slf4j +@Configuration +public class AsyncTaskExecutePool implements AsyncConfigurer { + + /** 注入配置类 */ + private final AsyncTaskProperties config; + + public AsyncTaskExecutePool(AsyncTaskProperties config) { + this.config = config; + } + + @Override + public Executor getAsyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + //核心线程池大小 + executor.setCorePoolSize(config.getCorePoolSize()); + //最大线程数 + executor.setMaxPoolSize(config.getMaxPoolSize()); + //队列容量 + executor.setQueueCapacity(config.getQueueCapacity()); + //活跃时间 + executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); + //线程名字前缀 + executor.setThreadNamePrefix("yxk-async-"); + // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务 + // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return (throwable, method, objects) -> { + log.error("===="+throwable.getMessage()+"====", throwable); + log.error("exception method:"+method.getName()); + }; + } +} diff --git a/system/src/main/java/com/canvas/web/config/thread/AsyncTaskProperties.java b/system/src/main/java/com/canvas/web/config/thread/AsyncTaskProperties.java new file mode 100644 index 0000000..6a56095 --- /dev/null +++ b/system/src/main/java/com/canvas/web/config/thread/AsyncTaskProperties.java @@ -0,0 +1,20 @@ +package com.canvas.web.config.thread; + + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "task.pool") +public class AsyncTaskProperties { + + private int corePoolSize; + + private int maxPoolSize; + + private int keepAliveSeconds; + + private int queueCapacity; +} diff --git a/system/src/main/java/com/canvas/web/config/thread/TheadFactoryName.java b/system/src/main/java/com/canvas/web/config/thread/TheadFactoryName.java new file mode 100644 index 0000000..e1dbe68 --- /dev/null +++ b/system/src/main/java/com/canvas/web/config/thread/TheadFactoryName.java @@ -0,0 +1,44 @@ +package com.canvas.web.config.thread; + + +import org.springframework.stereotype.Component; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +//自定义线程名字 +@Component +public class TheadFactoryName implements ThreadFactory { + private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; + + public TheadFactoryName() { + this("canvas-screen-pool"); + } + + private TheadFactoryName(String name){ + SecurityManager s = System.getSecurityManager(); + group = (s != null) ? s.getThreadGroup() : + Thread.currentThread().getThreadGroup(); + //此时namePrefix就是 name + 第几个用这个工厂创建线程池的 + this.namePrefix = name + + POOL_NUMBER.getAndIncrement(); + } + + @Override + public Thread newThread(Runnable r) { + //此时线程的名字 就是 namePrefix + -thread- + 这个线程池中第几个执行的线程 + Thread t = new Thread(group, r, + namePrefix + "-thread-"+threadNumber.getAndIncrement(), + 0); + if (t.isDaemon()) { + t.setDaemon(false); + } + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + return t; + } +} diff --git a/system/src/main/java/com/canvas/web/modules/config/SpringSecurityConfig.java b/system/src/main/java/com/canvas/web/modules/config/SpringSecurityConfig.java deleted file mode 100644 index 0ae5a63..0000000 --- a/system/src/main/java/com/canvas/web/modules/config/SpringSecurityConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.canvas.web.modules.config; - -import com.canvas.web.modules.security.JwtAccessDeniedHandler; -import com.canvas.web.modules.security.JwtAuthenticationEntryPoint; -import com.canvas.web.modules.security.SecurityProperties; -import com.canvas.web.modules.security.TokenProvider; -import lombok.RequiredArgsConstructor; - -import com.canvas.web.modules.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; -} diff --git a/system/src/main/java/com/canvas/web/modules/config/bean/SecurityProperties.java b/system/src/main/java/com/canvas/web/modules/config/bean/SecurityProperties.java deleted file mode 100644 index 0e8cd3a..0000000 --- a/system/src/main/java/com/canvas/web/modules/config/bean/SecurityProperties.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.canvas.web.modules.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+" "; - } -} diff --git a/system/src/main/java/com/canvas/web/modules/controller/AuthorizationController.java b/system/src/main/java/com/canvas/web/modules/controller/AuthorizationController.java index ab558a9..dfaabdb 100644 --- a/system/src/main/java/com/canvas/web/modules/controller/AuthorizationController.java +++ b/system/src/main/java/com/canvas/web/modules/controller/AuthorizationController.java @@ -4,9 +4,11 @@ package com.canvas.web.modules.controller; import com.canvas.web.annotation.rest.AnonymousPostMapping; import com.canvas.web.config.RsaProperties; import com.canvas.web.exception.BaseException; -import com.canvas.web.modules.security.TokenProvider; -import com.canvas.web.modules.service.OnlineUserService; -import com.canvas.web.modules.service.dto.AuthUserDto; +import com.canvas.web.modules.security.config.bean.SecurityProperties; +import com.canvas.web.modules.security.security.TokenProvider; +import com.canvas.web.modules.security.config.bean.LoginProperties; +import com.canvas.web.modules.security.service.OnlineUserService; +import com.canvas.web.modules.security.service.dto.AuthUserDto; import com.canvas.web.utils.RedisUtils; import com.canvas.web.utils.RsaUtils; import com.canvas.web.utils.StringUtils; @@ -14,9 +16,7 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import com.canvas.web.modules.config.bean.LoginProperties; -import com.canvas.web.modules.config.bean.SecurityProperties; -import com.canvas.web.modules.service.dto.JwtUserDto; +import com.canvas.web.modules.security.service.dto.JwtUserDto; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; diff --git a/system/src/main/java/com/canvas/web/modules/controller/OnlineController.java b/system/src/main/java/com/canvas/web/modules/controller/OnlineController.java index fc66b9b..2c72604 100644 --- a/system/src/main/java/com/canvas/web/modules/controller/OnlineController.java +++ b/system/src/main/java/com/canvas/web/modules/controller/OnlineController.java @@ -1,7 +1,7 @@ package com.canvas.web.modules.controller; -import com.canvas.web.modules.service.OnlineUserService; +import com.canvas.web.modules.security.service.OnlineUserService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; diff --git a/system/src/main/java/com/canvas/web/modules/config/ConfigBeanConfiguration.java b/system/src/main/java/com/canvas/web/modules/security/config/ConfigBeanConfiguration.java similarity index 76% rename from system/src/main/java/com/canvas/web/modules/config/ConfigBeanConfiguration.java rename to system/src/main/java/com/canvas/web/modules/security/config/ConfigBeanConfiguration.java index b32551c..0ffd1f4 100644 --- a/system/src/main/java/com/canvas/web/modules/config/ConfigBeanConfiguration.java +++ b/system/src/main/java/com/canvas/web/modules/security/config/ConfigBeanConfiguration.java @@ -1,8 +1,7 @@ -package com.canvas.web.modules.config; +package com.canvas.web.modules.security.config; - -import com.canvas.web.modules.config.bean.SecurityProperties; -import com.canvas.web.modules.config.bean.LoginProperties; +import com.canvas.web.modules.security.config.bean.LoginProperties; +import com.canvas.web.modules.security.config.bean.SecurityProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/system/src/main/java/com/canvas/web/modules/security/config/SpringSecurityConfig.java b/system/src/main/java/com/canvas/web/modules/security/config/SpringSecurityConfig.java new file mode 100644 index 0000000..bf532c3 --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/security/config/SpringSecurityConfig.java @@ -0,0 +1,178 @@ +package com.canvas.web.modules.security.config; + + +import com.canvas.web.annotation.AnonymousAccess; +import com.canvas.web.enums.RequestMethodEnum; +import com.canvas.web.modules.security.config.bean.SecurityProperties; +import com.canvas.web.modules.security.security.JwtAccessDeniedHandler; +import com.canvas.web.modules.security.security.JwtAuthenticationEntryPoint; +import com.canvas.web.modules.security.security.TokenConfigurer; +import com.canvas.web.modules.security.security.TokenProvider; +import com.canvas.web.modules.security.service.OnlineUserService; +import com.canvas.web.modules.security.service.UserCacheClean; +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.core.GrantedAuthorityDefaults; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.*; + +@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 OnlineUserService onlineUserService; + private final UserCacheClean userCacheClean; + + @Bean + GrantedAuthorityDefaults grantedAuthorityDefaults() { + // 去除 ROLE_ 前缀 + return new GrantedAuthorityDefaults(""); + } + + //密码交给框架管理 + @Bean + public PasswordEncoder passwordEncoder() { + // 密码加密方式 + return new BCryptPasswordEncoder(); + } + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + // 搜寻匿名标记 url: @AnonymousAccess + RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping"); + Map handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods(); + // 获取匿名标记 + Map> anonymousUrls = getAnonymousUrl(handlerMethodMap); + httpSecurity + // 禁用 CSRF + .csrf().disable() + .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) + // 授权异常 + .exceptionHandling() + .authenticationEntryPoint(authenticationErrorHandler) + .accessDeniedHandler(jwtAccessDeniedHandler) + // 防止iframe 造成跨域 + .and() + .headers() + .frameOptions() + .disable() + // 不创建会话 + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + // 放行资源 + .antMatchers( + HttpMethod.GET, + "/*.html", + "/**/*.html", + "/**/*.css", + "/**/*.js", + "/webSocket/**" + ).permitAll() + // swagger 文档 + .antMatchers("/swagger-ui.html").permitAll() + .antMatchers("/swagger-resources/**").permitAll() + .antMatchers("/webjars/**").permitAll() + .antMatchers("/*/api-docs").permitAll() + // 文件 + .antMatchers("/avatar/**").permitAll() + .antMatchers("/file/**").permitAll() + // 阿里巴巴 druid + .antMatchers("/druid/**").permitAll() + // 放行OPTIONS请求 + .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() + // 自定义匿名访问所有url放行:允许匿名和带Token访问,细腻化到每个 Request 类型 + // GET + .antMatchers(HttpMethod.GET, anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll() + // POST + .antMatchers(HttpMethod.POST, anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll() + // PUT + .antMatchers(HttpMethod.PUT, anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll() + // PATCH + .antMatchers(HttpMethod.PATCH, anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll() + // DELETE + .antMatchers(HttpMethod.DELETE, anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll() + // 所有类型的接口都放行 + .antMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(new String[0])).permitAll() + // 所有请求都需要认证 测试需求注释 + //.anyRequest().authenticated() + .and().apply(securityConfigurerAdapter()); + } + + private TokenConfigurer securityConfigurerAdapter() { + return new TokenConfigurer(tokenProvider, properties, onlineUserService, userCacheClean); + } + + private Map> getAnonymousUrl(Map handlerMethodMap) { + Map> anonymousUrls = new HashMap<>(6); + Set get = new HashSet<>(); + Set post = new HashSet<>(); + Set put = new HashSet<>(); + Set patch = new HashSet<>(); + Set delete = new HashSet<>(); + Set all = new HashSet<>(); + for (Map.Entry infoEntry : handlerMethodMap.entrySet()) { + HandlerMethod handlerMethod = infoEntry.getValue(); + AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class); + if (null != anonymousAccess) { + List requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods()); + RequestMethodEnum request = RequestMethodEnum.find(requestMethods.size() == 0 ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name()); + switch (Objects.requireNonNull(request)) { + case GET: + get.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + case POST: + post.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + case PUT: + put.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + case PATCH: + patch.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + case DELETE: + delete.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + default: + all.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + } + } + } + anonymousUrls.put(RequestMethodEnum.GET.getType(), get); + anonymousUrls.put(RequestMethodEnum.POST.getType(), post); + anonymousUrls.put(RequestMethodEnum.PUT.getType(), put); + anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch); + anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete); + anonymousUrls.put(RequestMethodEnum.ALL.getType(), all); + return anonymousUrls; + } +} diff --git a/system/src/main/java/com/canvas/web/modules/config/bean/LoginCode.java b/system/src/main/java/com/canvas/web/modules/security/config/bean/LoginCode.java similarity index 92% rename from system/src/main/java/com/canvas/web/modules/config/bean/LoginCode.java rename to system/src/main/java/com/canvas/web/modules/security/config/bean/LoginCode.java index 4db6e5f..13aedda 100644 --- a/system/src/main/java/com/canvas/web/modules/config/bean/LoginCode.java +++ b/system/src/main/java/com/canvas/web/modules/security/config/bean/LoginCode.java @@ -1,4 +1,5 @@ -package com.canvas.web.modules.config.bean; +package com.canvas.web.modules.security.config.bean; + import lombok.Data; diff --git a/system/src/main/java/com/canvas/web/modules/config/bean/LoginCodeEnum.java b/system/src/main/java/com/canvas/web/modules/security/config/bean/LoginCodeEnum.java similarity index 76% rename from system/src/main/java/com/canvas/web/modules/config/bean/LoginCodeEnum.java rename to system/src/main/java/com/canvas/web/modules/security/config/bean/LoginCodeEnum.java index 48a89f1..3b7e069 100644 --- a/system/src/main/java/com/canvas/web/modules/config/bean/LoginCodeEnum.java +++ b/system/src/main/java/com/canvas/web/modules/security/config/bean/LoginCodeEnum.java @@ -1,6 +1,5 @@ -package com.canvas.web.modules.config.bean; +package com.canvas.web.modules.security.config.bean; -//验证码配置枚举 public enum LoginCodeEnum { /** * 算术 diff --git a/system/src/main/java/com/canvas/web/modules/config/bean/LoginProperties.java b/system/src/main/java/com/canvas/web/modules/security/config/bean/LoginProperties.java similarity index 98% rename from system/src/main/java/com/canvas/web/modules/config/bean/LoginProperties.java rename to system/src/main/java/com/canvas/web/modules/security/config/bean/LoginProperties.java index 491eaad..7a6123b 100644 --- a/system/src/main/java/com/canvas/web/modules/config/bean/LoginProperties.java +++ b/system/src/main/java/com/canvas/web/modules/security/config/bean/LoginProperties.java @@ -1,4 +1,4 @@ -package com.canvas.web.modules.config.bean; +package com.canvas.web.modules.security.config.bean; import com.canvas.web.exception.BaseException; import com.canvas.web.utils.StringUtils; diff --git a/system/src/main/java/com/canvas/web/modules/security/SecurityProperties.java b/system/src/main/java/com/canvas/web/modules/security/config/bean/SecurityProperties.java similarity index 94% rename from system/src/main/java/com/canvas/web/modules/security/SecurityProperties.java rename to system/src/main/java/com/canvas/web/modules/security/config/bean/SecurityProperties.java index daad602..98e0c1f 100644 --- a/system/src/main/java/com/canvas/web/modules/security/SecurityProperties.java +++ b/system/src/main/java/com/canvas/web/modules/security/config/bean/SecurityProperties.java @@ -1,4 +1,4 @@ -package com.canvas.web.modules.security; +package com.canvas.web.modules.security.config.bean; import lombok.Data; diff --git a/system/src/main/java/com/canvas/web/modules/security/JwtAccessDeniedHandler.java b/system/src/main/java/com/canvas/web/modules/security/security/JwtAccessDeniedHandler.java similarity index 94% rename from system/src/main/java/com/canvas/web/modules/security/JwtAccessDeniedHandler.java rename to system/src/main/java/com/canvas/web/modules/security/security/JwtAccessDeniedHandler.java index 363466e..2de3be5 100644 --- a/system/src/main/java/com/canvas/web/modules/security/JwtAccessDeniedHandler.java +++ b/system/src/main/java/com/canvas/web/modules/security/security/JwtAccessDeniedHandler.java @@ -1,4 +1,4 @@ -package com.canvas.web.modules.security; +package com.canvas.web.modules.security.security; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; diff --git a/system/src/main/java/com/canvas/web/modules/security/JwtAuthenticationEntryPoint.java b/system/src/main/java/com/canvas/web/modules/security/security/JwtAuthenticationEntryPoint.java similarity index 93% rename from system/src/main/java/com/canvas/web/modules/security/JwtAuthenticationEntryPoint.java rename to system/src/main/java/com/canvas/web/modules/security/security/JwtAuthenticationEntryPoint.java index 4b32faa..f7db532 100644 --- a/system/src/main/java/com/canvas/web/modules/security/JwtAuthenticationEntryPoint.java +++ b/system/src/main/java/com/canvas/web/modules/security/security/JwtAuthenticationEntryPoint.java @@ -1,4 +1,4 @@ -package com.canvas.web.modules.security; +package com.canvas.web.modules.security.security; import org.springframework.security.core.AuthenticationException; diff --git a/system/src/main/java/com/canvas/web/modules/security/TokenConfigurer.java b/system/src/main/java/com/canvas/web/modules/security/security/TokenConfigurer.java similarity index 70% rename from system/src/main/java/com/canvas/web/modules/security/TokenConfigurer.java rename to system/src/main/java/com/canvas/web/modules/security/security/TokenConfigurer.java index 0c8f977..b9d7bf8 100644 --- a/system/src/main/java/com/canvas/web/modules/security/TokenConfigurer.java +++ b/system/src/main/java/com/canvas/web/modules/security/security/TokenConfigurer.java @@ -1,8 +1,9 @@ -package com.canvas.web.modules.security; +package com.canvas.web.modules.security.security; -import com.canvas.web.modules.service.OnlineUserService; +import com.canvas.web.modules.security.config.bean.SecurityProperties; +import com.canvas.web.modules.security.service.OnlineUserService; import lombok.RequiredArgsConstructor; -import com.canvas.web.modules.service.UserCacheClean; +import com.canvas.web.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; diff --git a/system/src/main/java/com/canvas/web/modules/security/security/TokenFilter.java b/system/src/main/java/com/canvas/web/modules/security/security/TokenFilter.java new file mode 100644 index 0000000..aea93cd --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/security/security/TokenFilter.java @@ -0,0 +1,91 @@ +package com.canvas.web.modules.security.security; + +import cn.hutool.core.util.StrUtil; +import com.canvas.web.modules.security.config.bean.SecurityProperties; +import com.canvas.web.modules.security.service.OnlineUserService; +import com.canvas.web.modules.security.service.UserCacheClean; +import com.canvas.web.modules.security.service.dto.OnlineUserDto; +import io.jsonwebtoken.ExpiredJwtException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.GenericFilterBean; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.Objects; + +public class TokenFilter extends GenericFilterBean { + + private static final Logger log= LoggerFactory.getLogger(TokenFilter.class); + + private final TokenProvider tokenProvider; + private final SecurityProperties properties; + private final OnlineUserService onlineUserService; + private final UserCacheClean userCacheClean; + + /** + * @param tokenProvider Token + * @param properties JWT + * @param onlineUserService 用户在线 + * @param userCacheClean 用户缓存清理工具 + */ + public TokenFilter(TokenProvider tokenProvider, SecurityProperties properties, OnlineUserService onlineUserService, UserCacheClean userCacheClean) { + this.properties = properties; + this.onlineUserService = onlineUserService; + this.tokenProvider = tokenProvider; + this.userCacheClean = userCacheClean; + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + String token = resolveToken(httpServletRequest); + // 对于 Token 为空的不需要去查 Redis + if (StrUtil.isNotBlank(token)) { + OnlineUserDto onlineUserDto = null; + boolean cleanUserCache = false; + try { + onlineUserDto = onlineUserService.getOne(properties.getOnlineKey() + token); + } catch (ExpiredJwtException e) { + log.error(e.getMessage()); + cleanUserCache = true; + } finally { + if (cleanUserCache || Objects.isNull(onlineUserDto)) { + userCacheClean.cleanUserCache(String.valueOf(tokenProvider.getClaims(token).get(TokenProvider.AUTHORITIES_KEY))); + } + } + if (onlineUserDto != null && StringUtils.hasText(token)) { + Authentication authentication = tokenProvider.getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + // Token 续期 + tokenProvider.checkRenewal(token); + } + } + filterChain.doFilter(servletRequest, servletResponse); + } + + /** + * 初步检测Token + * + * @param request / + * @return / + */ + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(properties.getHeader()); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(properties.getTokenStartWith())) { + // 去掉令牌前缀 + return bearerToken.replace(properties.getTokenStartWith(), ""); + } else { + log.debug("非法Token:{}", bearerToken); + } + return null; + } +} diff --git a/system/src/main/java/com/canvas/web/modules/security/TokenProvider.java b/system/src/main/java/com/canvas/web/modules/security/security/TokenProvider.java similarity index 96% rename from system/src/main/java/com/canvas/web/modules/security/TokenProvider.java rename to system/src/main/java/com/canvas/web/modules/security/security/TokenProvider.java index f9b3efb..9592e4b 100644 --- a/system/src/main/java/com/canvas/web/modules/security/TokenProvider.java +++ b/system/src/main/java/com/canvas/web/modules/security/security/TokenProvider.java @@ -1,8 +1,9 @@ -package com.canvas.web.modules.security; +package com.canvas.web.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.modules.security.config.bean.SecurityProperties; import com.canvas.web.utils.RedisUtils; import io.jsonwebtoken.*; diff --git a/system/src/main/java/com/canvas/web/modules/security/service/OnlineUserService.java b/system/src/main/java/com/canvas/web/modules/security/service/OnlineUserService.java new file mode 100644 index 0000000..493ba67 --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/security/service/OnlineUserService.java @@ -0,0 +1,168 @@ +package com.canvas.web.modules.security.service; + + +import com.canvas.web.modules.security.config.bean.SecurityProperties; +import com.canvas.web.utils.*; +import lombok.extern.slf4j.Slf4j; +import com.canvas.web.modules.security.service.dto.JwtUserDto; +import com.canvas.web.modules.security.service.dto.OnlineUserDto; +import org.springframework.data.domain.Pageable; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + + +@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; + } + + public void save(JwtUserDto jwtUserDto, String token, HttpServletRequest request) { + + //String dept = jwtUserDto.getUser().getName().getDept(); + String ip = StringUtils.getIp(request); + String browser = StringUtils.getBrowser(request); + String address = StringUtils.getCityInfo(ip); + OnlineUserDto onlineUserDto = null; + try { + //onlineUserDto = new OnlineUserDto(jwtUserDto.getUsername(), jwtUserDto.getUser().getNickName(), dept, browser , ip, address, EncryptUtils.desEncrypt(token), new Date()); + } catch (Exception e) { + log.error(e.getMessage(),e); + } + redisUtils.set(properties.getOnlineKey() + token, onlineUserDto, properties.getTokenValidityInSeconds()/1000); + } + + /** + * 查询全部数据 + * @param filter / + * @param pageable / + * @return / + */ + public Map getAll(String filter, Pageable pageable){ + List onlineUserDtos = getAll(filter); + return PageUtil.toPage( + PageUtil.toPage(pageable.getPageNumber(),pageable.getPageSize(), onlineUserDtos), + onlineUserDtos.size() + ); + } + + /** + * 查询全部数据,不分页 + * @param filter / + * @return / + */ + public List getAll(String filter){ + List keys = redisUtils.scan(properties.getOnlineKey() + "*"); + Collections.reverse(keys); + List onlineUserDtos = new ArrayList<>(); + for (String key : keys) { + OnlineUserDto onlineUserDto = (OnlineUserDto) redisUtils.get(key); + if(StringUtils.isNotBlank(filter)){ + if(onlineUserDto.toString().contains(filter)){ + onlineUserDtos.add(onlineUserDto); + } + } else { + onlineUserDtos.add(onlineUserDto); + } + } + onlineUserDtos.sort((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime())); + return onlineUserDtos; + } + + /** + * 踢出用户 + * @param key / + */ + public void kickOut(String key){ + key = properties.getOnlineKey() + key; + redisUtils.del(key); + } + + /** + * 退出登录 + * @param token / + */ + public void logout(String token) { + String key = properties.getOnlineKey() + token; + redisUtils.del(key); + } + + /** + * 导出 + * @param all / + * @param response / + * @throws IOException / + */ + public void download(List all, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (OnlineUserDto user : all) { + Map map = new LinkedHashMap<>(); + map.put("用户名", user.getUserName()); + map.put("部门", user.getDept()); + map.put("登录IP", user.getIp()); + map.put("登录地点", user.getAddress()); + map.put("浏览器", user.getBrowser()); + map.put("登录日期", user.getLoginTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } + + /** + * 查询用户 + * @param key / + * @return / + */ + public OnlineUserDto getOne(String key) { + return (OnlineUserDto)redisUtils.get(key); + } + + /** + * 检测用户是否在之前已经登录,已经登录踢下线 + * @param userName 用户名 + */ + public void checkLoginOnUser(String userName, String igoreToken){ + List onlineUserDtos = getAll(userName); + if(onlineUserDtos ==null || onlineUserDtos.isEmpty()){ + return; + } + for(OnlineUserDto onlineUserDto : onlineUserDtos){ + if(onlineUserDto.getUserName().equals(userName)){ + try { + String token = EncryptUtils.desDecrypt(onlineUserDto.getKey()); + if(StringUtils.isNotBlank(igoreToken)&&!igoreToken.equals(token)){ + this.kickOut(token); + }else if(StringUtils.isBlank(igoreToken)){ + this.kickOut(token); + } + } catch (Exception e) { + log.error("checkUser is error",e); + } + } + } + } + + /** + * 根据用户名强退用户 + * @param username / + */ + @Async + public void kickOutForUsername(String username) { + List onlineUsers = getAll(username); + for (OnlineUserDto onlineUser : onlineUsers) { + if (onlineUser.getUserName().equals(username)) { + kickOut(onlineUser.getKey()); + } + } + } +} diff --git a/system/src/main/java/com/canvas/web/modules/service/UserCacheClean.java b/system/src/main/java/com/canvas/web/modules/security/service/UserCacheClean.java similarity index 93% rename from system/src/main/java/com/canvas/web/modules/service/UserCacheClean.java rename to system/src/main/java/com/canvas/web/modules/security/service/UserCacheClean.java index 7822a2a..41c5209 100644 --- a/system/src/main/java/com/canvas/web/modules/service/UserCacheClean.java +++ b/system/src/main/java/com/canvas/web/modules/security/service/UserCacheClean.java @@ -1,4 +1,4 @@ -package com.canvas.web.modules.service; +package com.canvas.web.modules.security.service; diff --git a/system/src/main/java/com/canvas/web/modules/service/UserDetailsServiceImpl.java b/system/src/main/java/com/canvas/web/modules/security/service/UserDetailsServiceImpl.java similarity index 93% rename from system/src/main/java/com/canvas/web/modules/service/UserDetailsServiceImpl.java rename to system/src/main/java/com/canvas/web/modules/security/service/UserDetailsServiceImpl.java index 6b5af9b..0a464c4 100644 --- a/system/src/main/java/com/canvas/web/modules/service/UserDetailsServiceImpl.java +++ b/system/src/main/java/com/canvas/web/modules/security/service/UserDetailsServiceImpl.java @@ -1,11 +1,11 @@ -package com.canvas.web.modules.service; +package com.canvas.web.modules.security.service; import com.canvas.web.exception.BaseException; -import com.canvas.web.modules.service.dto.JwtUserDto; +import com.canvas.web.modules.security.config.bean.LoginProperties; +import com.canvas.web.modules.security.service.dto.JwtUserDto; import com.canvas.web.modules.system.service.UserService; import com.canvas.web.modules.system.service.dto.UserDto; import lombok.RequiredArgsConstructor; -import com.canvas.web.modules.config.bean.LoginProperties; import com.canvas.web.modules.system.service.DataService; import com.canvas.web.modules.system.service.RoleService; diff --git a/system/src/main/java/com/canvas/web/modules/service/dto/AuthUserDto.java b/system/src/main/java/com/canvas/web/modules/security/service/dto/AuthUserDto.java similarity index 84% rename from system/src/main/java/com/canvas/web/modules/service/dto/AuthUserDto.java rename to system/src/main/java/com/canvas/web/modules/security/service/dto/AuthUserDto.java index 89cc046..4373cf8 100644 --- a/system/src/main/java/com/canvas/web/modules/service/dto/AuthUserDto.java +++ b/system/src/main/java/com/canvas/web/modules/security/service/dto/AuthUserDto.java @@ -1,4 +1,4 @@ -package com.canvas.web.modules.service.dto; +package com.canvas.web.modules.security.service.dto; import lombok.Getter; diff --git a/system/src/main/java/com/canvas/web/modules/service/dto/JwtUserDto.java b/system/src/main/java/com/canvas/web/modules/security/service/dto/JwtUserDto.java similarity index 96% rename from system/src/main/java/com/canvas/web/modules/service/dto/JwtUserDto.java rename to system/src/main/java/com/canvas/web/modules/security/service/dto/JwtUserDto.java index 26b369c..04deafe 100644 --- a/system/src/main/java/com/canvas/web/modules/service/dto/JwtUserDto.java +++ b/system/src/main/java/com/canvas/web/modules/security/service/dto/JwtUserDto.java @@ -1,4 +1,4 @@ -package com.canvas.web.modules.service.dto; +package com.canvas.web.modules.security.service.dto; import com.canvas.web.modules.system.service.dto.UserDto; diff --git a/system/src/main/java/com/canvas/web/modules/service/dto/OnlineUserDto.java b/system/src/main/java/com/canvas/web/modules/security/service/dto/OnlineUserDto.java similarity index 92% rename from system/src/main/java/com/canvas/web/modules/service/dto/OnlineUserDto.java rename to system/src/main/java/com/canvas/web/modules/security/service/dto/OnlineUserDto.java index ed14d77..0af1e5e 100644 --- a/system/src/main/java/com/canvas/web/modules/service/dto/OnlineUserDto.java +++ b/system/src/main/java/com/canvas/web/modules/security/service/dto/OnlineUserDto.java @@ -1,4 +1,4 @@ -package com.canvas.web.modules.service.dto; +package com.canvas.web.modules.security.service.dto; import lombok.AllArgsConstructor; diff --git a/system/src/main/java/com/canvas/web/modules/service/OnlineUserService.java b/system/src/main/java/com/canvas/web/modules/service/OnlineUserService.java deleted file mode 100644 index c71a402..0000000 --- a/system/src/main/java/com/canvas/web/modules/service/OnlineUserService.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.canvas.web.modules.service; - -import com.canvas.web.utils.RedisUtils; -import com.canvas.web.utils.StringUtils; -import lombok.extern.slf4j.Slf4j; -import com.canvas.web.modules.config.bean.SecurityProperties; -import com.canvas.web.modules.service.dto.JwtUserDto; -import com.canvas.web.modules.service.dto.OnlineUserDto; -import org.springframework.stereotype.Service; - -import javax.servlet.http.HttpServletRequest; - - -@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; - } - - public void save(JwtUserDto jwtUserDto, String token, HttpServletRequest request) { - - //String dept = jwtUserDto.getUser().getName().getDept(); - String ip = StringUtils.getIp(request); - String browser = StringUtils.getBrowser(request); - String address = StringUtils.getCityInfo(ip); - OnlineUserDto onlineUserDto = null; - try { - //onlineUserDto = new OnlineUserDto(jwtUserDto.getUsername(), jwtUserDto.getUser().getNickName(), dept, browser , ip, address, EncryptUtils.desEncrypt(token), new Date()); - } catch (Exception e) { - log.error(e.getMessage(),e); - } - redisUtils.set(properties.getOnlineKey() + token, onlineUserDto, properties.getTokenValidityInSeconds()/1000); - } - - public void checkLoginOnUser(String username, String token) { - - } -} diff --git a/system/src/main/java/com/canvas/web/modules/system/domain/Menu.java b/system/src/main/java/com/canvas/web/modules/system/domain/Menu.java new file mode 100644 index 0000000..83fb1c0 --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/system/domain/Menu.java @@ -0,0 +1,89 @@ +package com.canvas.web.modules.system.domain; + +import com.canvas.web.base.BaseEntity; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModelProperty; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; +import java.util.Set; + +public class Menu extends BaseEntity implements Serializable { + + @Id + @Column(name = "menu_id") + @NotNull(groups = {Update.class}) + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @JsonIgnore + @ManyToMany(mappedBy = "menus") + @ApiModelProperty(value = "菜单角色") + private Set roles; + + @ApiModelProperty(value = "菜单标题") + private String title; + + @Column(name = "name") + @ApiModelProperty(value = "菜单组件名称") + private String componentName; + + @ApiModelProperty(value = "排序") + private Integer menuSort = 999; + + @ApiModelProperty(value = "组件路径") + private String component; + + @ApiModelProperty(value = "路由地址") + private String path; + + @ApiModelProperty(value = "菜单类型,目录、菜单、按钮") + private Integer type; + + @ApiModelProperty(value = "权限标识") + private String permission; + + @ApiModelProperty(value = "菜单图标") + private String icon; + + @Column(columnDefinition = "bit(1) default 0") + @ApiModelProperty(value = "缓存") + private Boolean cache; + + @Column(columnDefinition = "bit(1) default 0") + @ApiModelProperty(value = "是否隐藏") + private Boolean hidden; + + @ApiModelProperty(value = "上级菜单") + private Long pid; + + @ApiModelProperty(value = "子节点数目", hidden = true) + private Integer subCount = 0; + + @ApiModelProperty(value = "外链菜单") + private Boolean iFrame; + + @Column(name = "show_position") + @ApiModelProperty(value = "显示地方 1.侧面 2.头部") + private Integer showPosition; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Menu menu = (Menu) o; + return Objects.equals(id, menu.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/system/src/main/java/com/canvas/web/modules/system/domain/Org.java b/system/src/main/java/com/canvas/web/modules/system/domain/Org.java new file mode 100644 index 0000000..cbf8d4b --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/system/domain/Org.java @@ -0,0 +1,8 @@ +package com.canvas.web.modules.system.domain; + +import com.canvas.web.base.BaseEntity; + +import java.io.Serializable; + +public class Org extends BaseEntity implements Serializable { +} diff --git a/system/src/main/java/com/canvas/web/modules/system/domain/Role.java b/system/src/main/java/com/canvas/web/modules/system/domain/Role.java new file mode 100644 index 0000000..d059d39 --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/system/domain/Role.java @@ -0,0 +1,79 @@ +package com.canvas.web.modules.system.domain; + +import com.canvas.web.base.BaseEntity; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; +import java.util.Set; + + +@Getter +@Setter +@Entity +@Table(name = "sys_role") +public class Role extends BaseEntity implements Serializable { + + @Id + @Column(name = "role_id") + @NotNull(groups = {Update.class}) + @GeneratedValue(strategy = GenerationType.IDENTITY) + @ApiModelProperty(value = "ID", hidden = true) + private Long id; + + @JsonIgnore + @ManyToMany(mappedBy = "roles") + @ApiModelProperty(value = "用户", hidden = true) + private Set users; + + @ManyToMany + @JoinTable(name = "sys_roles_menus", + joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")}, + inverseJoinColumns = {@JoinColumn(name = "menu_id",referencedColumnName = "menu_id")}) + @ApiModelProperty(value = "菜单", hidden = true) + private Set menus; + +// @ManyToMany +// @JoinTable(name = "sys_roles_depts", +// joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")}, +// inverseJoinColumns = {@JoinColumn(name = "dept_id",referencedColumnName = "dept_id")}) +// @ApiModelProperty(value = "部门", hidden = true) +// private Set depts; + + @NotBlank + @ApiModelProperty(value = "名称", hidden = true) + private String name; + +// @ApiModelProperty(value = "数据权限,全部 、 本级 、 自定义") +// private String dataScope = DataScopeEnum.THIS_LEVEL.getValue(); + + @Column(name = "level") + @ApiModelProperty(value = "级别,数值越小,级别越大") + private Integer level = 3; + + @ApiModelProperty(value = "描述") + private String description; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Role role = (Role) o; + return Objects.equals(id, role.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/system/src/main/java/com/canvas/web/modules/system/domain/User.java b/system/src/main/java/com/canvas/web/modules/system/domain/User.java new file mode 100644 index 0000000..9c89af0 --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/system/domain/User.java @@ -0,0 +1,104 @@ +package com.canvas.web.modules.system.domain; + +import com.canvas.web.base.BaseEntity; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; +import java.util.Objects; +import java.util.Set; + + +@Entity +@Getter +@Setter +@Table(name="sys_user") +public class User extends BaseEntity implements Serializable { + + @Id + @Column(name = "user_id") + @NotNull(groups = Update.class) + @GeneratedValue(strategy = GenerationType.IDENTITY) + @ApiModelProperty(value = "ID", hidden = true) + private Long id; + + + @ManyToMany + @ApiModelProperty(value = "用户角色") + @JoinTable(name = "sys_users_roles", + joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "user_id")}, + inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")}) + private Set roles; + + + +// @OneToOne +// @JoinColumn(name = "dept_id") +// @ApiModelProperty(value = "用户部门") +// private Org dept; + + @NotBlank + @Column(unique = true) + @ApiModelProperty(value = "用户名称") + private String username; + + @NotBlank + @ApiModelProperty(value = "用户昵称") + private String nickName; + + @Email + @NotBlank + @ApiModelProperty(value = "邮箱") + private String email; + + @NotBlank + @ApiModelProperty(value = "电话号码") + private String phone; + + @ApiModelProperty(value = "用户性别") + private String gender; + + @ApiModelProperty(value = "头像真实名称",hidden = true) + private String avatarName; + + @ApiModelProperty(value = "头像存储的路径", hidden = true) + private String avatarPath; + + @ApiModelProperty(value = "密码") + private String password; + + @NotNull + @ApiModelProperty(value = "是否启用") + private Boolean enabled; + + @ApiModelProperty(value = "是否为admin账号", hidden = true) + private Boolean isAdmin = false; + + @Column(name = "pwd_reset_time") + @ApiModelProperty(value = "最后修改密码的时间", hidden = true) + private Date pwdResetTime; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + User user = (User) o; + return Objects.equals(id, user.id) && + Objects.equals(username, user.username); + } + + @Override + public int hashCode() { + return Objects.hash(id, username); + } +} diff --git a/system/src/main/java/com/canvas/web/modules/system/repository/MenuRepository.java b/system/src/main/java/com/canvas/web/modules/system/repository/MenuRepository.java new file mode 100644 index 0000000..ace0116 --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/system/repository/MenuRepository.java @@ -0,0 +1,4 @@ +package com.canvas.web.modules.system.repository; + +public interface MenuRepository { +} diff --git a/system/src/main/java/com/canvas/web/modules/system/repository/OrgRepository.java b/system/src/main/java/com/canvas/web/modules/system/repository/OrgRepository.java new file mode 100644 index 0000000..f81dcda --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/system/repository/OrgRepository.java @@ -0,0 +1,4 @@ +package com.canvas.web.modules.system.repository; + +public interface OrgRepository { +} diff --git a/system/src/main/java/com/canvas/web/modules/system/repository/RoleRepository.java b/system/src/main/java/com/canvas/web/modules/system/repository/RoleRepository.java new file mode 100644 index 0000000..76173c0 --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/system/repository/RoleRepository.java @@ -0,0 +1,4 @@ +package com.canvas.web.modules.system.repository; + +public interface RoleRepository { +} diff --git a/system/src/main/java/com/canvas/web/modules/system/repository/UserRepository.java b/system/src/main/java/com/canvas/web/modules/system/repository/UserRepository.java new file mode 100644 index 0000000..fa5d6b7 --- /dev/null +++ b/system/src/main/java/com/canvas/web/modules/system/repository/UserRepository.java @@ -0,0 +1,4 @@ +package com.canvas.web.modules.system.repository; + +public interface UserRepository { +}