41 changed files with 1233 additions and 156 deletions
-
67common/src/main/java/com/canvas/web/base/BaseEntity.java
-
43common/src/main/java/com/canvas/web/config/FileProperties.java
-
52common/src/main/java/com/canvas/web/enums/RequestMethodEnum.java
-
79common/src/main/java/com/canvas/web/utils/EncryptUtils.java
-
46common/src/main/java/com/canvas/web/utils/PageUtil.java
-
7system/pom.xml
-
47system/src/main/java/com/canvas/web/config/ConfigurerAdapter.java
-
14system/src/main/java/com/canvas/web/config/WebSocketConfig.java
-
51system/src/main/java/com/canvas/web/config/thread/AsyncTaskExecutePool.java
-
20system/src/main/java/com/canvas/web/config/thread/AsyncTaskProperties.java
-
44system/src/main/java/com/canvas/web/config/thread/TheadFactoryName.java
-
32system/src/main/java/com/canvas/web/modules/config/SpringSecurityConfig.java
-
52system/src/main/java/com/canvas/web/modules/config/bean/SecurityProperties.java
-
12system/src/main/java/com/canvas/web/modules/controller/AuthorizationController.java
-
2system/src/main/java/com/canvas/web/modules/controller/OnlineController.java
-
7system/src/main/java/com/canvas/web/modules/security/config/ConfigBeanConfiguration.java
-
178system/src/main/java/com/canvas/web/modules/security/config/SpringSecurityConfig.java
-
3system/src/main/java/com/canvas/web/modules/security/config/bean/LoginCode.java
-
3system/src/main/java/com/canvas/web/modules/security/config/bean/LoginCodeEnum.java
-
2system/src/main/java/com/canvas/web/modules/security/config/bean/LoginProperties.java
-
2system/src/main/java/com/canvas/web/modules/security/config/bean/SecurityProperties.java
-
2system/src/main/java/com/canvas/web/modules/security/security/JwtAccessDeniedHandler.java
-
2system/src/main/java/com/canvas/web/modules/security/security/JwtAuthenticationEntryPoint.java
-
7system/src/main/java/com/canvas/web/modules/security/security/TokenConfigurer.java
-
91system/src/main/java/com/canvas/web/modules/security/security/TokenFilter.java
-
3system/src/main/java/com/canvas/web/modules/security/security/TokenProvider.java
-
168system/src/main/java/com/canvas/web/modules/security/service/OnlineUserService.java
-
2system/src/main/java/com/canvas/web/modules/security/service/UserCacheClean.java
-
6system/src/main/java/com/canvas/web/modules/security/service/UserDetailsServiceImpl.java
-
2system/src/main/java/com/canvas/web/modules/security/service/dto/AuthUserDto.java
-
2system/src/main/java/com/canvas/web/modules/security/service/dto/JwtUserDto.java
-
2system/src/main/java/com/canvas/web/modules/security/service/dto/OnlineUserDto.java
-
43system/src/main/java/com/canvas/web/modules/service/OnlineUserService.java
-
89system/src/main/java/com/canvas/web/modules/system/domain/Menu.java
-
8system/src/main/java/com/canvas/web/modules/system/domain/Org.java
-
79system/src/main/java/com/canvas/web/modules/system/domain/Role.java
-
104system/src/main/java/com/canvas/web/modules/system/domain/User.java
-
4system/src/main/java/com/canvas/web/modules/system/repository/MenuRepository.java
-
4system/src/main/java/com/canvas/web/modules/system/repository/OrgRepository.java
-
4system/src/main/java/com/canvas/web/modules/system/repository/RoleRepository.java
-
4system/src/main/java/com/canvas/web/modules/system/repository/UserRepository.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(); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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<String,Object> toPage(Page page) { |
||||
|
Map<String,Object> map = new LinkedHashMap<>(2); |
||||
|
map.put("content",page.getContent()); |
||||
|
map.put("totalElements",page.getTotalElements()); |
||||
|
return map; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 自定义分页 |
||||
|
*/ |
||||
|
public static Map<String,Object> toPage(Object object, Object totalElements) { |
||||
|
Map<String,Object> map = new LinkedHashMap<>(2); |
||||
|
map.put("content",object); |
||||
|
map.put("totalElements",totalElements); |
||||
|
return map; |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
} |
@ -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()); |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
|
||||
} |
|
@ -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+" "; |
|
||||
} |
|
||||
} |
|
@ -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.boot.context.properties.ConfigurationProperties; |
||||
import org.springframework.context.annotation.Bean; |
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
import org.springframework.context.annotation.Configuration; |
@ -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<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods(); |
||||
|
// 获取匿名标记 |
||||
|
Map<String, Set<String>> 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<String, Set<String>> getAnonymousUrl(Map<RequestMappingInfo, HandlerMethod> handlerMethodMap) { |
||||
|
Map<String, Set<String>> anonymousUrls = new HashMap<>(6); |
||||
|
Set<String> get = new HashSet<>(); |
||||
|
Set<String> post = new HashSet<>(); |
||||
|
Set<String> put = new HashSet<>(); |
||||
|
Set<String> patch = new HashSet<>(); |
||||
|
Set<String> delete = new HashSet<>(); |
||||
|
Set<String> all = new HashSet<>(); |
||||
|
for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) { |
||||
|
HandlerMethod handlerMethod = infoEntry.getValue(); |
||||
|
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class); |
||||
|
if (null != anonymousAccess) { |
||||
|
List<RequestMethod> 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; |
||||
|
} |
||||
|
} |
@ -1,4 +1,5 @@ |
|||||
package com.canvas.web.modules.config.bean; |
|
||||
|
package com.canvas.web.modules.security.config.bean; |
||||
|
|
||||
|
|
||||
import lombok.Data; |
import lombok.Data; |
||||
|
|
@ -1,6 +1,5 @@ |
|||||
package com.canvas.web.modules.config.bean; |
|
||||
|
package com.canvas.web.modules.security.config.bean; |
||||
|
|
||||
//验证码配置枚举 |
|
||||
public enum LoginCodeEnum { |
public enum LoginCodeEnum { |
||||
/** |
/** |
||||
* 算术 |
* 算术 |
@ -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.exception.BaseException; |
||||
import com.canvas.web.utils.StringUtils; |
import com.canvas.web.utils.StringUtils; |
@ -1,4 +1,4 @@ |
|||||
package com.canvas.web.modules.security; |
|
||||
|
package com.canvas.web.modules.security.config.bean; |
||||
|
|
||||
|
|
||||
import lombok.Data; |
import lombok.Data; |
@ -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.access.AccessDeniedException; |
||||
import org.springframework.security.web.access.AccessDeniedHandler; |
import org.springframework.security.web.access.AccessDeniedHandler; |
@ -1,4 +1,4 @@ |
|||||
package com.canvas.web.modules.security; |
|
||||
|
package com.canvas.web.modules.security.security; |
||||
|
|
||||
|
|
||||
import org.springframework.security.core.AuthenticationException; |
import org.springframework.security.core.AuthenticationException; |
@ -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 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.SecurityConfigurerAdapter; |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
||||
import org.springframework.security.web.DefaultSecurityFilterChain; |
import org.springframework.security.web.DefaultSecurityFilterChain; |
@ -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; |
||||
|
} |
||||
|
} |
@ -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.DateField; |
||||
import cn.hutool.core.date.DateUtil; |
import cn.hutool.core.date.DateUtil; |
||||
import cn.hutool.core.util.IdUtil; |
import cn.hutool.core.util.IdUtil; |
||||
|
import com.canvas.web.modules.security.config.bean.SecurityProperties; |
||||
import com.canvas.web.utils.RedisUtils; |
import com.canvas.web.utils.RedisUtils; |
||||
|
|
||||
import io.jsonwebtoken.*; |
import io.jsonwebtoken.*; |
@ -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<String,Object> getAll(String filter, Pageable pageable){ |
||||
|
List<OnlineUserDto> onlineUserDtos = getAll(filter); |
||||
|
return PageUtil.toPage( |
||||
|
PageUtil.toPage(pageable.getPageNumber(),pageable.getPageSize(), onlineUserDtos), |
||||
|
onlineUserDtos.size() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 查询全部数据,不分页 |
||||
|
* @param filter / |
||||
|
* @return / |
||||
|
*/ |
||||
|
public List<OnlineUserDto> getAll(String filter){ |
||||
|
List<String> keys = redisUtils.scan(properties.getOnlineKey() + "*"); |
||||
|
Collections.reverse(keys); |
||||
|
List<OnlineUserDto> 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<OnlineUserDto> all, HttpServletResponse response) throws IOException { |
||||
|
List<Map<String, Object>> list = new ArrayList<>(); |
||||
|
for (OnlineUserDto user : all) { |
||||
|
Map<String,Object> 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<OnlineUserDto> 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<OnlineUserDto> onlineUsers = getAll(username); |
||||
|
for (OnlineUserDto onlineUser : onlineUsers) { |
||||
|
if (onlineUser.getUserName().equals(username)) { |
||||
|
kickOut(onlineUser.getKey()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -1,4 +1,4 @@ |
|||||
package com.canvas.web.modules.service; |
|
||||
|
package com.canvas.web.modules.security.service; |
||||
|
|
||||
|
|
||||
|
|
@ -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.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.UserService; |
||||
import com.canvas.web.modules.system.service.dto.UserDto; |
import com.canvas.web.modules.system.service.dto.UserDto; |
||||
import lombok.RequiredArgsConstructor; |
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.DataService; |
||||
import com.canvas.web.modules.system.service.RoleService; |
import com.canvas.web.modules.system.service.RoleService; |
||||
|
|
@ -1,4 +1,4 @@ |
|||||
package com.canvas.web.modules.service.dto; |
|
||||
|
package com.canvas.web.modules.security.service.dto; |
||||
|
|
||||
|
|
||||
import lombok.Getter; |
import lombok.Getter; |
@ -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; |
import com.canvas.web.modules.system.service.dto.UserDto; |
@ -1,4 +1,4 @@ |
|||||
package com.canvas.web.modules.service.dto; |
|
||||
|
package com.canvas.web.modules.security.service.dto; |
||||
|
|
||||
|
|
||||
import lombok.AllArgsConstructor; |
import lombok.AllArgsConstructor; |
@ -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) { |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -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<Role> 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); |
||||
|
} |
||||
|
} |
@ -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 { |
||||
|
} |
@ -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<User> 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<Menu> 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<Dept> 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); |
||||
|
} |
||||
|
} |
@ -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<Role> 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); |
||||
|
} |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
package com.canvas.web.modules.system.repository; |
||||
|
|
||||
|
public interface MenuRepository { |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
package com.canvas.web.modules.system.repository; |
||||
|
|
||||
|
public interface OrgRepository { |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
package com.canvas.web.modules.system.repository; |
||||
|
|
||||
|
public interface RoleRepository { |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
package com.canvas.web.modules.system.repository; |
||||
|
|
||||
|
public interface UserRepository { |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue