Merge remote-tracking branch 'remote/master'

# Conflicts:
#	eladmin-system/src/main/java/me/zhengjie/modules/quartz/task/TestTask.java
pull/725/head
Emil.Zhang 2022-05-31 17:55:59 +08:00
commit 42efa2e744
26 changed files with 250 additions and 377 deletions

View File

@ -80,10 +80,18 @@ public class RedisConfig extends CachingConfigurerSupport {
// value值的序列化采用fastJsonRedisSerializer // value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer); template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer); template.setHashValueSerializer(fastJsonRedisSerializer);
// 全局开启AutoType这里方便开发使用全局的方式 // fastjson 升级到 1.2.83 后需要指定序列化白名单
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain");
// 建议使用这种方式,小范围指定白名单 ParserConfig.getGlobalInstance().addAccept("me.zhengjie.service.dto");
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain"); // 模块内的实体类
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.mnt.domain");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.quartz.domain");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.system.domain");
// 模块内的 Dto
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.mnt.service.dto");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.quartz.service.dto");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.security.service.dto");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.system.service.dto");
// key的序列化采用StringRedisSerializer // key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer()); template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer());

View File

@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*; import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.*; import java.util.*;
@ -41,6 +42,9 @@ public class RedisUtils {
public RedisUtils(RedisTemplate<Object, Object> redisTemplate) { public RedisUtils(RedisTemplate<Object, Object> redisTemplate) {
this.redisTemplate = redisTemplate; this.redisTemplate = redisTemplate;
this.redisTemplate.setHashKeySerializer(new StringRedisSerializer());
this.redisTemplate.setKeySerializer(new StringRedisSerializer());
this.redisTemplate.setStringSerializer(new StringRedisSerializer());
} }
/** /**

View File

@ -71,7 +71,7 @@ public class LogController {
@ApiOperation("用户日志查询") @ApiOperation("用户日志查询")
public ResponseEntity<Object> queryUserLog(LogQueryCriteria criteria, Pageable pageable){ public ResponseEntity<Object> queryUserLog(LogQueryCriteria criteria, Pageable pageable){
criteria.setLogType("INFO"); criteria.setLogType("INFO");
criteria.setBlurry(SecurityUtils.getCurrentUsername()); criteria.setUsername(SecurityUtils.getCurrentUsername());
return new ResponseEntity<>(logService.queryAllByUser(criteria,pageable), HttpStatus.OK); return new ResponseEntity<>(logService.queryAllByUser(criteria,pageable), HttpStatus.OK);
} }

View File

@ -31,6 +31,9 @@ public class LogQueryCriteria {
@Query(blurry = "username,description,address,requestIp,method,params") @Query(blurry = "username,description,address,requestIp,method,params")
private String blurry; private String blurry;
@Query
private String username;
@Query @Query
private String logType; private String logType;

View File

@ -24,7 +24,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner; import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.List; import java.util.List;
/** /**
@ -45,9 +44,8 @@ public class JobRunner implements ApplicationRunner {
*/ */
@Override @Override
public void run(ApplicationArguments applicationArguments) { public void run(ApplicationArguments applicationArguments) {
log.info("--------------------注入系统定时任务------------------");
List<QuartzJob> quartzJobs = quartzJobRepository.findByIsPauseIsFalse(); List<QuartzJob> quartzJobs = quartzJobRepository.findByIsPauseIsFalse();
quartzJobs.forEach(quartzManage::addJob); quartzJobs.forEach(quartzManage::addJob);
log.info("--------------------定时任务注入完成------------------"); log.info("Timing task injection complete");
} }
} }

View File

@ -20,7 +20,7 @@ import me.zhengjie.annotation.AnonymousAccess;
import me.zhengjie.modules.security.config.bean.SecurityProperties; import me.zhengjie.modules.security.config.bean.SecurityProperties;
import me.zhengjie.modules.security.security.*; import me.zhengjie.modules.security.security.*;
import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.OnlineUserService;
import me.zhengjie.modules.security.service.UserCacheClean; import me.zhengjie.modules.security.service.UserCacheManager;
import me.zhengjie.utils.enums.RequestMethodEnum; import me.zhengjie.utils.enums.RequestMethodEnum;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -58,7 +58,7 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
private final SecurityProperties properties; private final SecurityProperties properties;
private final OnlineUserService onlineUserService; private final OnlineUserService onlineUserService;
private final UserCacheClean userCacheClean; private final UserCacheManager userCacheManager;
@Bean @Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() { GrantedAuthorityDefaults grantedAuthorityDefaults() {
@ -138,7 +138,7 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
} }
private TokenConfigurer securityConfigurerAdapter() { private TokenConfigurer securityConfigurerAdapter() {
return new TokenConfigurer(tokenProvider, properties, onlineUserService, userCacheClean); return new TokenConfigurer(tokenProvider, properties, onlineUserService, userCacheManager);
} }
private Map<String, Set<String>> getAnonymousUrl(Map<RequestMappingInfo, HandlerMethod> handlerMethodMap) { private Map<String, Set<String>> getAnonymousUrl(Map<RequestMappingInfo, HandlerMethod> handlerMethodMap) {

View File

@ -20,7 +20,6 @@ import com.wf.captcha.base.Captcha;
import lombok.Data; import lombok.Data;
import me.zhengjie.exception.BadConfigurationException; import me.zhengjie.exception.BadConfigurationException;
import me.zhengjie.utils.StringUtils; import me.zhengjie.utils.StringUtils;
import java.awt.*; import java.awt.*;
import java.util.Objects; import java.util.Objects;
@ -40,19 +39,12 @@ public class LoginProperties {
private LoginCode loginCode; private LoginCode loginCode;
/** public static final String cacheKey = "USER-LOGIN-DATA";
*
*/
private boolean cacheEnable;
public boolean isSingleLogin() { public boolean isSingleLogin() {
return singleLogin; return singleLogin;
} }
public boolean isCacheEnable() {
return cacheEnable;
}
/** /**
* *
* *

View File

@ -18,7 +18,7 @@ package me.zhengjie.modules.security.security;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.security.config.bean.SecurityProperties; import me.zhengjie.modules.security.config.bean.SecurityProperties;
import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.OnlineUserService;
import me.zhengjie.modules.security.service.UserCacheClean; import me.zhengjie.modules.security.service.UserCacheManager;
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;
@ -33,11 +33,11 @@ public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFi
private final TokenProvider tokenProvider; private final TokenProvider tokenProvider;
private final SecurityProperties properties; private final SecurityProperties properties;
private final OnlineUserService onlineUserService; private final OnlineUserService onlineUserService;
private final UserCacheClean userCacheClean; private final UserCacheManager userCacheManager;
@Override @Override
public void configure(HttpSecurity http) { public void configure(HttpSecurity http) {
TokenFilter customFilter = new TokenFilter(tokenProvider, properties, onlineUserService, userCacheClean); TokenFilter customFilter = new TokenFilter(tokenProvider, properties, onlineUserService, userCacheManager);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
} }
} }

View File

@ -18,7 +18,7 @@ package me.zhengjie.modules.security.security;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.ExpiredJwtException;
import me.zhengjie.modules.security.config.bean.SecurityProperties; import me.zhengjie.modules.security.config.bean.SecurityProperties;
import me.zhengjie.modules.security.service.UserCacheClean; import me.zhengjie.modules.security.service.UserCacheManager;
import me.zhengjie.modules.security.service.dto.OnlineUserDto; import me.zhengjie.modules.security.service.dto.OnlineUserDto;
import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.OnlineUserService;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -27,7 +27,6 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean; import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
@ -46,19 +45,19 @@ public class TokenFilter extends GenericFilterBean {
private final TokenProvider tokenProvider; private final TokenProvider tokenProvider;
private final SecurityProperties properties; private final SecurityProperties properties;
private final OnlineUserService onlineUserService; private final OnlineUserService onlineUserService;
private final UserCacheClean userCacheClean; private final UserCacheManager userCacheManager;
/** /**
* @param tokenProvider Token * @param tokenProvider Token
* @param properties JWT * @param properties JWT
* @param onlineUserService 线 * @param onlineUserService 线
* @param userCacheClean * @param userCacheManager
*/ */
public TokenFilter(TokenProvider tokenProvider, SecurityProperties properties, OnlineUserService onlineUserService, UserCacheClean userCacheClean) { public TokenFilter(TokenProvider tokenProvider, SecurityProperties properties, OnlineUserService onlineUserService, UserCacheManager userCacheManager) {
this.properties = properties; this.properties = properties;
this.onlineUserService = onlineUserService; this.onlineUserService = onlineUserService;
this.tokenProvider = tokenProvider; this.tokenProvider = tokenProvider;
this.userCacheClean = userCacheClean; this.userCacheManager = userCacheManager;
} }
@Override @Override
@ -77,7 +76,7 @@ public class TokenFilter extends GenericFilterBean {
cleanUserCache = true; cleanUserCache = true;
} finally { } finally {
if (cleanUserCache || Objects.isNull(onlineUserDto)) { if (cleanUserCache || Objects.isNull(onlineUserDto)) {
userCacheClean.cleanUserCache(String.valueOf(tokenProvider.getClaims(token).get(TokenProvider.AUTHORITIES_KEY))); userCacheManager.cleanUserCache(String.valueOf(tokenProvider.getClaims(token).get(TokenProvider.AUTHORITIES_KEY)));
} }
} }
if (onlineUserDto != null && StringUtils.hasText(token)) { if (onlineUserDto != null && StringUtils.hasText(token)) {

View File

@ -1,53 +0,0 @@
/*
* Copyright 2019-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.security.service;
import lombok.AllArgsConstructor;
import me.zhengjie.utils.StringUtils;
import org.springframework.stereotype.Component;
/**
* @author: liaojinlong
* @date: 2020/6/11 18:01
* @apiNote: Spring
*/
@Component
@AllArgsConstructor
public class UserCacheClean {
private final UserCacheManager userCacheManager;
/**
* <br>
*
*
* @param userName /
*/
public void cleanUserCache(String userName) {
if (StringUtils.isNotEmpty(userName)) {
userCacheManager.remove(userName);
}
}
/**
* <br>
* ,便
*/
public void cleanAll() {
userCacheManager.clear();
}
}

View File

@ -1,110 +1,82 @@
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.security.service; package me.zhengjie.modules.security.service;
import lombok.extern.slf4j.Slf4j; import cn.hutool.core.util.RandomUtil;
import me.zhengjie.modules.security.config.bean.LoginProperties;
import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.modules.security.service.dto.JwtUserDto;
import me.zhengjie.utils.RedisUtils;
import me.zhengjie.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* * @author Zheng Jie
* * @description
* @author TikiWong * @date 2022-05-26
* @date 2022/1/27 8:23
**/ **/
@Slf4j
@Component @Component
public class UserCacheManager { public class UserCacheManager {
@Value("${user-cache.min-evictable-size}") @Resource
private int minEvictableSize; private RedisUtils redisUtils;
@Value("${user-cache.min-evictable-interval}") @Value("${login.user-cache.idle-time}")
private long minEvictableInterval; private long idleTime;
@Value("${user-cache.min-idle-time}")
private long minIdleTime;
private final Map<String, Node> cache = new ConcurrentHashMap<>(); /**
private final AtomicBoolean expelLock = new AtomicBoolean(true); *
private long nextMinEvictableTime = 0; * @param userName
* @return JwtUserDto
public Future<JwtUserDto> putIfAbsent(String username, Future<JwtUserDto> ft) { */
Node tryNode = new Node(ft); public JwtUserDto getUserCache(String userName) {
Node node = cache.putIfAbsent(username, tryNode); if (StringUtils.isNotEmpty(userName)) {
expel(); // 获取数据
return nodeToDate(node); Object obj = redisUtils.hget(LoginProperties.cacheKey, userName);
if(obj != null){
return (JwtUserDto)obj;
}
}
return null;
} }
/** /**
* * Redis
* * @param userName
* */
**/ @Async
public void expel() { public void addUserCache(String userName, JwtUserDto user) {
long now = System.currentTimeMillis(); if (StringUtils.isNotEmpty(userName)) {
if (cache.size() < minEvictableSize || // 添加数据, 避免数据同时过期
now < nextMinEvictableTime || long time = idleTime + RandomUtil.randomInt(900, 1800);
!expelLock.compareAndSet(true, false)) { redisUtils.hset(LoginProperties.cacheKey, userName, user, time);
return;
}
long oldestTime = now;
int evictedCount = 0;
try {
Iterator<Map.Entry<String, Node>> iterator = cache.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Node> entry = iterator.next();
long nodeTime = entry.getValue().getTime();
if (nodeTime + minIdleTime < now) {
iterator.remove();
evictedCount++;
}
oldestTime = Math.min(oldestTime, nodeTime);
}
} finally {
this.nextMinEvictableTime = Math.max(now + minEvictableInterval, oldestTime);
expelLock.set(true);
log.info("回收掉【{}】条用户缓存, 剩余缓存数为【{}】,下次可回收时间为【{}】秒后",
evictedCount,
cache.size(),
(this.nextMinEvictableTime - now) / 1000);
} }
} }
public Future<JwtUserDto> get(String username) { /**
return nodeToDate(cache.get(username)); *
} *
* @param userName
public void clear() { */
cache.clear(); @Async
} public void cleanUserCache(String userName) {
if (StringUtils.isNotEmpty(userName)) {
public void remove(String username) { // 清除数据
cache.remove(username); redisUtils.hdel(LoginProperties.cacheKey, userName);
}
private Future<JwtUserDto> nodeToDate(Node node) {
return node == null ? null : node.getData();
}
private static class Node {
private final Future<JwtUserDto> data;
private final long time;
public Node(Future<JwtUserDto> data) {
this.data = data;
this.time = System.currentTimeMillis();
}
public Future<JwtUserDto> getData() {
return data;
}
public long getTime() {
return time;
} }
} }
} }

View File

@ -16,50 +16,38 @@
package me.zhengjie.modules.security.service; package me.zhengjie.modules.security.service;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.exception.BadRequestException; import me.zhengjie.exception.BadRequestException;
import me.zhengjie.exception.EntityNotFoundException; import me.zhengjie.exception.EntityNotFoundException;
import me.zhengjie.modules.security.config.bean.LoginProperties;
import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.modules.security.service.dto.JwtUserDto;
import me.zhengjie.modules.system.service.DataService; import me.zhengjie.modules.system.service.DataService;
import me.zhengjie.modules.system.service.RoleService; import me.zhengjie.modules.system.service.RoleService;
import me.zhengjie.modules.system.service.UserService; import me.zhengjie.modules.system.service.UserService;
import me.zhengjie.modules.system.service.dto.UserDto; import me.zhengjie.modules.system.service.dto.UserLoginDto;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/** /**
* @author Zheng Jie * @author Zheng Jie
* @date 2018-11-22 * @date 2018-11-22
*/ */
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Service("userDetailsService") @Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService { public class UserDetailsServiceImpl implements UserDetailsService {
private final UserService userService; private final UserService userService;
private final RoleService roleService; private final RoleService roleService;
private final DataService dataService; private final DataService dataService;
private final LoginProperties loginProperties; private final UserCacheManager userCacheManager;
private final UserCacheManager USER_DTO_CACHE;
public void setEnableCache(boolean enableCache) {
this.loginProperties.setCacheEnable(enableCache);
}
public static ExecutorService executor = newThreadPool();
@Override @Override
public JwtUserDto loadUserByUsername(String username) { public JwtUserDto loadUserByUsername(String username) {
JwtUserDto jwtUserDto = null; JwtUserDto jwtUserDto = userCacheManager.getUserCache(username);
Future<JwtUserDto> future = USER_DTO_CACHE.get(username); if(jwtUserDto == null){
if (!loginProperties.isCacheEnable()) { UserLoginDto user;
UserDto user;
try { try {
user = userService.findByName(username); user = userService.getLoginData(username);
} catch (EntityNotFoundException e) { } catch (EntityNotFoundException e) {
// SpringSecurity会自动转换UsernameNotFoundException为BadCredentialsException // SpringSecurity会自动转换UsernameNotFoundException为BadCredentialsException
throw new UsernameNotFoundException(username, e); throw new UsernameNotFoundException(username, e);
@ -75,85 +63,10 @@ public class UserDetailsServiceImpl implements UserDetailsService {
dataService.getDeptIds(user), dataService.getDeptIds(user),
roleService.mapToGrantedAuthorities(user) roleService.mapToGrantedAuthorities(user)
); );
// 添加缓存数据
userCacheManager.addUserCache(username, jwtUserDto);
} }
return jwtUserDto;
}
if (future == null) {
Callable<JwtUserDto> call = () -> getJwtBySearchDb(username);
FutureTask<JwtUserDto> ft = new FutureTask<>(call);
future = USER_DTO_CACHE.putIfAbsent(username, ft);
if (future == null) {
future = ft;
executor.submit(ft);
}
try {
return future.get();
} catch (CancellationException e) {
USER_DTO_CACHE.remove(username);
System.out.println("error" + Thread.currentThread().getName());
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
jwtUserDto = future.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e.getMessage());
}
// 检查dataScope是否修改
List<Long> dataScopes = jwtUserDto.getDataScopes();
dataScopes.clear();
dataScopes.addAll(dataService.getDeptIds(jwtUserDto.getUser()));
} }
return jwtUserDto; return jwtUserDto;
}
private JwtUserDto getJwtBySearchDb(String username) {
UserDto user;
try {
user = userService.findByName(username);
} catch (EntityNotFoundException e) {
// SpringSecurity会自动转换UsernameNotFoundException为BadCredentialsException
throw new UsernameNotFoundException("", e);
}
if (user == null) {
throw new UsernameNotFoundException("");
} else {
if (!user.getEnabled()) {
throw new BadRequestException("账号未激活!");
}
return new JwtUserDto(
user,
dataService.getDeptIds(user),
roleService.mapToGrantedAuthorities(user)
);
}
}
public static ExecutorService newThreadPool() {
ThreadFactory namedThreadFactory = new ThreadFactory() {
final AtomicInteger sequence = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
int seq = this.sequence.getAndIncrement();
thread.setName("future-task-thread" + (seq > 1 ? "-" + seq : ""));
if (!thread.isDaemon()) {
thread.setDaemon(true);
}
return thread;
}
};
return new ThreadPoolExecutor(10, 200,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1024),
namedThreadFactory,
new ThreadPoolExecutor.AbortPolicy());
} }
} }

View File

@ -0,0 +1,34 @@
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.security.service.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
/**
*
* @author Zheng Jie
* @date 2018-11-30
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AuthorityDto implements GrantedAuthority {
private String authority;
}

View File

@ -18,8 +18,7 @@ package me.zhengjie.modules.security.service.dto;
import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import me.zhengjie.modules.system.service.dto.UserDto; import me.zhengjie.modules.system.service.dto.UserLoginDto;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -33,15 +32,14 @@ import java.util.stream.Collectors;
@AllArgsConstructor @AllArgsConstructor
public class JwtUserDto implements UserDetails { public class JwtUserDto implements UserDetails {
private final UserDto user; private final UserLoginDto user;
private final List<Long> dataScopes; private final List<Long> dataScopes;
@JSONField(serialize = false) private final List<AuthorityDto> authorities;
private final List<GrantedAuthority> authorities;
public Set<String> getRoles() { public Set<String> getRoles() {
return authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet()); return authorities.stream().map(AuthorityDto::getAuthority).collect(Collectors.toSet());
} }
@Override @Override

View File

@ -15,13 +15,13 @@
*/ */
package me.zhengjie.modules.system.service; package me.zhengjie.modules.system.service;
import me.zhengjie.modules.security.service.dto.AuthorityDto;
import me.zhengjie.modules.system.domain.Role; import me.zhengjie.modules.system.domain.Role;
import me.zhengjie.modules.system.service.dto.RoleDto; import me.zhengjie.modules.system.service.dto.RoleDto;
import me.zhengjie.modules.system.service.dto.RoleQueryCriteria; import me.zhengjie.modules.system.service.dto.RoleQueryCriteria;
import me.zhengjie.modules.system.service.dto.RoleSmallDto; import me.zhengjie.modules.system.service.dto.RoleSmallDto;
import me.zhengjie.modules.system.service.dto.UserDto; import me.zhengjie.modules.system.service.dto.UserDto;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.security.core.GrantedAuthority;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -119,7 +119,7 @@ public interface RoleService {
* @param user * @param user
* @return * @return
*/ */
List<GrantedAuthority> mapToGrantedAuthorities(UserDto user); List<AuthorityDto> mapToGrantedAuthorities(UserDto user);
/** /**
* *

View File

@ -17,6 +17,7 @@ package me.zhengjie.modules.system.service;
import me.zhengjie.modules.system.domain.User; import me.zhengjie.modules.system.domain.User;
import me.zhengjie.modules.system.service.dto.UserDto; import me.zhengjie.modules.system.service.dto.UserDto;
import me.zhengjie.modules.system.service.dto.UserLoginDto;
import me.zhengjie.modules.system.service.dto.UserQueryCriteria; import me.zhengjie.modules.system.service.dto.UserQueryCriteria;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -65,6 +66,13 @@ public interface UserService {
*/ */
UserDto findByName(String userName); UserDto findByName(String userName);
/**
*
* @param userName /
* @return /
*/
UserLoginDto getLoginData(String userName);
/** /**
* *
* @param username * @param username

View File

@ -0,0 +1,28 @@
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.service.dto;
/**
* @author Zheng Jie
* @description 使
* @date 2022-05-26
**/
public class UserLoginDto extends UserDto {
private String password;
private Boolean isAdmin;
}

View File

@ -18,7 +18,8 @@ package me.zhengjie.modules.system.service.impl;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import me.zhengjie.exception.BadRequestException; import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.security.service.UserCacheClean; import me.zhengjie.modules.security.service.UserCacheManager;
import me.zhengjie.modules.security.service.dto.AuthorityDto;
import me.zhengjie.modules.system.domain.Menu; import me.zhengjie.modules.system.domain.Menu;
import me.zhengjie.modules.system.domain.Role; import me.zhengjie.modules.system.domain.Role;
import me.zhengjie.exception.EntityExistException; import me.zhengjie.exception.EntityExistException;
@ -38,11 +39,8 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
@ -62,7 +60,7 @@ public class RoleServiceImpl implements RoleService {
private final RoleSmallMapper roleSmallMapper; private final RoleSmallMapper roleSmallMapper;
private final RedisUtils redisUtils; private final RedisUtils redisUtils;
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserCacheClean userCacheClean; private final UserCacheManager userCacheManager;
@Override @Override
public List<RoleDto> queryAll() { public List<RoleDto> queryAll() {
@ -166,19 +164,19 @@ public class RoleServiceImpl implements RoleService {
@Override @Override
@Cacheable(key = "'auth:' + #p0.id") @Cacheable(key = "'auth:' + #p0.id")
public List<GrantedAuthority> mapToGrantedAuthorities(UserDto user) { public List<AuthorityDto> mapToGrantedAuthorities(UserDto user) {
Set<String> permissions = new HashSet<>(); Set<String> permissions = new HashSet<>();
// 如果是管理员直接返回 // 如果是管理员直接返回
if (user.getIsAdmin()) { if (user.getIsAdmin()) {
permissions.add("admin"); permissions.add("admin");
return permissions.stream().map(SimpleGrantedAuthority::new) return permissions.stream().map(AuthorityDto::new)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
Set<Role> roles = roleRepository.findByUserId(user.getId()); Set<Role> roles = roleRepository.findByUserId(user.getId());
permissions = roles.stream().flatMap(role -> role.getMenus().stream()) permissions = roles.stream().flatMap(role -> role.getMenus().stream())
.filter(menu -> StringUtils.isNotBlank(menu.getPermission())) .map(Menu::getPermission)
.map(Menu::getPermission).collect(Collectors.toSet()); .filter(StringUtils::isNotBlank).collect(Collectors.toSet());
return permissions.stream().map(SimpleGrantedAuthority::new) return permissions.stream().map(AuthorityDto::new)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -215,7 +213,7 @@ public class RoleServiceImpl implements RoleService {
public void delCaches(Long id, List<User> users) { public void delCaches(Long id, List<User> users) {
users = CollectionUtil.isEmpty(users) ? userRepository.findByRoleId(id) : users; users = CollectionUtil.isEmpty(users) ? userRepository.findByRoleId(id) : users;
if (CollectionUtil.isNotEmpty(users)) { if (CollectionUtil.isNotEmpty(users)) {
users.forEach(item -> userCacheClean.cleanUserCache(item.getUsername())); users.forEach(item -> userCacheManager.cleanUserCache(item.getUsername()));
Set<Long> userIds = users.stream().map(User::getId).collect(Collectors.toSet()); Set<Long> userIds = users.stream().map(User::getId).collect(Collectors.toSet());
redisUtils.delByKeys(CacheKey.DATA_USER, userIds); redisUtils.delByKeys(CacheKey.DATA_USER, userIds);
redisUtils.delByKeys(CacheKey.MENU_USER, userIds); redisUtils.delByKeys(CacheKey.MENU_USER, userIds);

View File

@ -19,16 +19,14 @@ import lombok.RequiredArgsConstructor;
import me.zhengjie.config.FileProperties; import me.zhengjie.config.FileProperties;
import me.zhengjie.exception.BadRequestException; import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.OnlineUserService;
import me.zhengjie.modules.security.service.UserCacheClean; import me.zhengjie.modules.security.service.UserCacheManager;
import me.zhengjie.modules.system.domain.User; import me.zhengjie.modules.system.domain.User;
import me.zhengjie.exception.EntityExistException; import me.zhengjie.exception.EntityExistException;
import me.zhengjie.exception.EntityNotFoundException; import me.zhengjie.exception.EntityNotFoundException;
import me.zhengjie.modules.system.repository.UserRepository; import me.zhengjie.modules.system.repository.UserRepository;
import me.zhengjie.modules.system.service.UserService; import me.zhengjie.modules.system.service.UserService;
import me.zhengjie.modules.system.service.dto.JobSmallDto; import me.zhengjie.modules.system.service.dto.*;
import me.zhengjie.modules.system.service.dto.RoleSmallDto; import me.zhengjie.modules.system.service.mapstruct.UserLoginMapper;
import me.zhengjie.modules.system.service.dto.UserDto;
import me.zhengjie.modules.system.service.dto.UserQueryCriteria;
import me.zhengjie.modules.system.service.mapstruct.UserMapper; import me.zhengjie.modules.system.service.mapstruct.UserMapper;
import me.zhengjie.utils.*; import me.zhengjie.utils.*;
import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheConfig;
@ -58,8 +56,9 @@ public class UserServiceImpl implements UserService {
private final UserMapper userMapper; private final UserMapper userMapper;
private final FileProperties properties; private final FileProperties properties;
private final RedisUtils redisUtils; private final RedisUtils redisUtils;
private final UserCacheClean userCacheClean; private final UserCacheManager userCacheManager;
private final OnlineUserService onlineUserService; private final OnlineUserService onlineUserService;
private final UserLoginMapper userLoginMapper;
@Override @Override
public Object queryAll(UserQueryCriteria criteria, Pageable pageable) { public Object queryAll(UserQueryCriteria criteria, Pageable pageable) {
@ -175,6 +174,16 @@ public class UserServiceImpl implements UserService {
} }
} }
@Override
public UserLoginDto getLoginData(String userName) {
User user = userRepository.findByUsername(userName);
if (user == null) {
throw new EntityNotFoundException(User.class, "name", userName);
} else {
return userLoginMapper.toDto(user);
}
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updatePass(String username, String pass) { public void updatePass(String username, String pass) {
@ -252,6 +261,6 @@ public class UserServiceImpl implements UserService {
* @param username / * @param username /
*/ */
private void flushCache(String username) { private void flushCache(String username) {
userCacheClean.cleanUserCache(username); userCacheManager.cleanUserCache(username);
} }
} }

View File

@ -0,0 +1,30 @@
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.modules.system.service.mapstruct;
import me.zhengjie.base.BaseMapper;
import me.zhengjie.modules.system.domain.User;
import me.zhengjie.modules.system.service.dto.UserLoginDto;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
/**
* @author Zheng Jie
* @date 2018-11-23
*/
@Mapper(componentModel = "spring",uses = {RoleMapper.class, DeptMapper.class, JobMapper.class},unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface UserLoginMapper extends BaseMapper<UserLoginDto, User> {
}

View File

@ -5,4 +5,4 @@
| __| | | (_| | (_| | | | | | | | | | | | __| | | (_| | (_| | | | | | | | | | |
\___|_| \__,_|\__,_|_| |_| |_|_|_| |_| \___|_| \__,_|\__,_|_| |_| |_|_|_| |_|
:: Spring Boot :: (v2.1.0.RELEASE) :: Spring Boot :: (v2.2.10.RELEASE)

View File

@ -51,10 +51,12 @@ spring:
# 登录相关配置 # 登录相关配置
login: login:
# 登录缓存
cache-enable: true
# 是否限制单用户登录 # 是否限制单用户登录
single-login: false single-login: false
# Redis用户登录缓存配置
user-cache:
# 存活时间/秒
idle-time: 7200
# 验证码 # 验证码
login-code: login-code:
# 验证码类型配置 查看 LoginProperties 类 # 验证码类型配置 查看 LoginProperties 类

View File

@ -33,6 +33,7 @@ spring:
webStatFilter: webStatFilter:
enabled: true enabled: true
stat-view-servlet: stat-view-servlet:
allow:
enabled: true enabled: true
# 控制台管理用户名和密码 # 控制台管理用户名和密码
url-pattern: /druid/* url-pattern: /druid/*
@ -52,10 +53,12 @@ spring:
# 登录相关配置 # 登录相关配置
login: login:
# 登录缓存
cache-enable: true
# 是否限制单用户登录 # 是否限制单用户登录
single-login: false single-login: false
# Redis用户登录缓存配置
user-cache:
# 存活时间/秒
idle-time: 7200
# 验证码 # 验证码
login-code: login-code:
# 验证码类型配置 查看 LoginProperties 类 # 验证码类型配置 查看 LoginProperties 类
@ -93,7 +96,7 @@ jwt:
# IP 本地解析 # IP 本地解析
ip: ip:
local-parsing: false local-parsing: true
#是否允许生成代码生产环境设置为false #是否允许生成代码生产环境设置为false
generator: generator:

View File

@ -56,12 +56,3 @@ code:
#密码加密传输,前端公钥加密,后端私钥解密 #密码加密传输,前端公钥加密,后端私钥解密
rsa: rsa:
private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A== private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==
# 内存用户缓存配置
user-cache:
# 最小回收数(当缓存数量达到此值时进行回收)
min-evictable-size: 512
# 最小回收间隔
min-evictable-interval: 1800000
# 最小存活时间 (ms)
min-idle-time: 3600000

View File

@ -1,64 +0,0 @@
package me.zhengjie;
import me.zhengjie.modules.security.service.UserDetailsServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class LoginCacheTest {
@Resource(name = "userDetailsService")
private UserDetailsServiceImpl userDetailsService;
ExecutorService executor = Executors.newCachedThreadPool();
@Test
public void testCache() throws InterruptedException {
long start1 = System.currentTimeMillis();
int size = 1000;
CountDownLatch latch = new CountDownLatch(size);
for (int i = 0; i < size; i++) {
executor.submit(() -> userDetailsService.loadUserByUsername("admin"));
latch.countDown();
}
latch.await();
long end1 = System.currentTimeMillis();
//关闭缓存
userDetailsService.setEnableCache(false);
long start2 = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
userDetailsService.loadUserByUsername("admin");
}
long end2 = System.currentTimeMillis();
System.out.print("使用缓存:" + (end1 - start1) + "毫秒\n 不使用缓存:" + (end2 - start2) + "毫秒");
}
@Test
public void testCacheManager() throws InterruptedException {
int size = 1000;
CountDownLatch latch = new CountDownLatch(size);
for (int i = 0; i < size; i++) {
int mod = i % 10;
executor.submit(() -> {
try {
Thread.sleep(mod * 2 + (int) (Math.random() * 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
userDetailsService.loadUserByUsername("admin" + mod);
latch.countDown();
System.out.println("剩余未完成数量" + latch.getCount());
});
}
latch.await();
}
}

View File

@ -34,7 +34,7 @@
<java.version>1.8</java.version> <java.version>1.8</java.version>
<log4jdbc.version>1.16</log4jdbc.version> <log4jdbc.version>1.16</log4jdbc.version>
<swagger.version>2.9.2</swagger.version> <swagger.version>2.9.2</swagger.version>
<fastjson.version>1.2.70</fastjson.version> <fastjson.version>1.2.83</fastjson.version>
<druid.version>1.1.24</druid.version> <druid.version>1.1.24</druid.version>
<commons-pool2.version>2.5.0</commons-pool2.version> <commons-pool2.version>2.5.0</commons-pool2.version>
<mapstruct.version>1.3.1.Final</mapstruct.version> <mapstruct.version>1.3.1.Final</mapstruct.version>