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.context.annotation.Bean; |
|||
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; |
|||
|
@ -1,6 +1,5 @@ |
|||
package com.canvas.web.modules.config.bean; |
|||
package com.canvas.web.modules.security.config.bean; |
|||
|
|||
//验证码配置枚举 |
|||
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.utils.StringUtils; |
@ -1,4 +1,4 @@ |
|||
package com.canvas.web.modules.security; |
|||
package com.canvas.web.modules.security.config.bean; |
|||
|
|||
|
|||
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.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; |
@ -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; |
@ -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.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.*; |
@ -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.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; |
|||
|
@ -1,4 +1,4 @@ |
|||
package com.canvas.web.modules.service.dto; |
|||
package com.canvas.web.modules.security.service.dto; |
|||
|
|||
|
|||
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; |
@ -1,4 +1,4 @@ |
|||
package com.canvas.web.modules.service.dto; |
|||
package com.canvas.web.modules.security.service.dto; |
|||
|
|||
|
|||
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