diff --git a/pear-common/pom.xml b/pear-common/pom.xml index 635dd41b3fcfbca10ac508d60fd30342fe54d6d8..d6b159167cc09bab7f60aa87d5eb606f689bd0bb 100644 --- a/pear-common/pom.xml +++ b/pear-common/pom.xml @@ -68,10 +68,21 @@ commons-io commons-io + + + + org.apache.commons + commons-pool2 + + com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.9.9 + + + + org.springframework.boot + spring-boot-starter-data-redis diff --git a/pear-common/src/main/java/com/pearadmin/common/config/RedisConfig.java b/pear-common/src/main/java/com/pearadmin/common/config/RedisConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..ff88a3ca3a0e564003e1b63ed37fa47f19e43c37 --- /dev/null +++ b/pear-common/src/main/java/com/pearadmin/common/config/RedisConfig.java @@ -0,0 +1,30 @@ +package com.pearadmin.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.io.Serializable; + +/** + * Describe: redis 序列化配置 + * Author: John Ming + * CreateTime: 2020/11/22 + * */ +@Configuration +public class RedisConfig { + + @Bean + public RedisTemplate + redisTemplate(LettuceConnectionFactory connectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + redisTemplate.setConnectionFactory(connectionFactory); + return redisTemplate; + } + +} diff --git a/pear-entrance/src/main/java/com/pearadmin/security/SecurityConfig.java b/pear-entrance/src/main/java/com/pearadmin/security/SecurityConfig.java index e8089ffeb202539d62c36af73ecb588ccb2c1da5..44d4de0e7cc2a9b7c077defd182bbfd2569ed19b 100644 --- a/pear-entrance/src/main/java/com/pearadmin/security/SecurityConfig.java +++ b/pear-entrance/src/main/java/com/pearadmin/security/SecurityConfig.java @@ -3,6 +3,7 @@ package com.pearadmin.security; import com.pearadmin.common.config.proprety.SecurityProperty; import com.pearadmin.security.domain.SecurityUserDetailsService; import com.pearadmin.security.process.*; +import com.pearadmin.security.support.RedisTokenRepositor; import com.pearadmin.security.support.SecurityPermissionEvaluator; import com.pearadmin.security.support.SecurityVerifyCodeFilter; import org.springframework.beans.factory.annotation.Autowired; @@ -19,8 +20,6 @@ import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; -import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import javax.annotation.Resource; import javax.sql.DataSource; @@ -62,8 +61,12 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SecurityUserDetailsService securityUserDetailsService; //实现userservice - @Resource - private DataSource dataSource; // 数据源 + + @Autowired + private RedisTokenRepositor redisTokenRepositor; + +// @Resource +// private DataSource dataSource; // 数据源 /** * Describe: 自定义权限注解实现 @@ -92,18 +95,24 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { } - /** - * 持久化token - * - * Security中,默认是使用PersistentTokenRepository的子类InMemoryTokenRepositoryImpl,将token放在内存中 - * 如果使用JdbcTokenRepositoryImpl,会创建表persistent_logins,将token持久化到数据库 - */ +// /** +// * 持久化token +// * +// * Security中,默认是使用PersistentTokenRepository的子类InMemoryTokenRepositoryImpl,将token放在内存中 +// * 如果使用JdbcTokenRepositoryImpl,会创建表persistent_logins,将token持久化到数据库 +// */ +// @Bean +// public PersistentTokenRepository persistentTokenRepository() { +// JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); +// tokenRepository.setDataSource(dataSource); // 设置数据源 +//// tokenRepository.setCreateTableOnStartup(true); // 启动创建表,创建成功后注释掉 +// return tokenRepository; +// } + + /** 注册SessionRegistry*/ @Bean - public PersistentTokenRepository persistentTokenRepository() { - JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); - tokenRepository.setDataSource(dataSource); // 设置数据源 -// tokenRepository.setCreateTableOnStartup(true); // 启动创建表,创建成功后注释掉 - return tokenRepository; + public SessionRegistry sessionRegistry(){ + return new SessionRegistryImpl(); } @@ -135,12 +144,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .logoutUrl("/logout") //配置用户登出自定义处理类 .logoutSuccessHandler(securityAccessLogoutHander) + .deleteCookies("remember-me") //退出登录删除 cookie缓存的rememberMe .and() //配置没有权限自定义处理类 .exceptionHandling().accessDeniedHandler(securityAccessDeniedHander) .and() .rememberMe().rememberMeParameter("remember-me") - .tokenRepository(persistentTokenRepository()) + .tokenRepository(redisTokenRepositor) .key(securityProperty.getRememberKey()) .and() // 防止iframe 造成跨域 @@ -151,19 +161,12 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { // 取消跨站请求伪造防护 .csrf().disable() .sessionManagement() - .invalidSessionUrl("/login") //无效session要跳转到login + .invalidSessionUrl("/login?kickout=1") //无效session要跳转到login .maximumSessions(1)//同时登陆多个只保留一个 .expiredUrl("/login")//过期session跳转 - .sessionRegistry(sessionRegistry()) - -; + .sessionRegistry(sessionRegistry()); http.headers().frameOptions().disable(); } - /** 注册SessionRegistry*/ - @Bean - public SessionRegistry sessionRegistry(){ - return new SessionRegistryImpl(); - } } diff --git a/pear-entrance/src/main/java/com/pearadmin/security/support/RedisTokenRepositor.java b/pear-entrance/src/main/java/com/pearadmin/security/support/RedisTokenRepositor.java new file mode 100644 index 0000000000000000000000000000000000000000..86b26217428d22b90a5e1d19660d6cce6d23aa7f --- /dev/null +++ b/pear-entrance/src/main/java/com/pearadmin/security/support/RedisTokenRepositor.java @@ -0,0 +1,100 @@ +package com.pearadmin.security.support; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken; +import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Describe: redis remember me 持久化到redis + * Author: John Ming + * CreateTime: 2020/11/22 + * */ +@Component +public class RedisTokenRepositor implements PersistentTokenRepository { + + private final static String USERNAME_KEY = "spring:security:rememberMe:username_key:"; + private final static String SERIES_KEY = "spring:security:rememberMe:series_key:"; + + @Autowired + RedisTemplate redisTemplate; + + @Autowired + StringRedisTemplate stringRedisTemplate; + @Override + public void createNewToken(PersistentRememberMeToken persistentRememberMeToken) { + String series = persistentRememberMeToken.getSeries(); + String key = generateKey(series,SERIES_KEY); + String usernameKey = generateKey(persistentRememberMeToken.getUsername(),USERNAME_KEY); + //用户只要采用账户密码重新登录,那么为了安全就有必要清除之前的token信息。deleteIfPresent方法通过 + //username查找到用户对应的series,然后删除旧的token信息。 + deleteIfPresent(usernameKey); + HashMap hashMap = new HashMap<>(); + hashMap.put("username",persistentRememberMeToken.getUsername()); + hashMap.put("token",persistentRememberMeToken.getTokenValue()); + hashMap.put("date",String.valueOf(persistentRememberMeToken.getDate().getTime())); + HashOperations hashOperations = redisTemplate.opsForHash(); + hashOperations.putAll(key,hashMap); + redisTemplate.expire(key,1, TimeUnit.DAYS);//设置token保存期限 + stringRedisTemplate.opsForValue().set(usernameKey,series); + redisTemplate.expire(usernameKey,1, TimeUnit.DAYS); + } + + @Override + public void updateToken(String s, String s1, Date date) { + String key = generateKey(s,SERIES_KEY); + if(redisTemplate.hasKey(key)) + redisTemplate.opsForHash().put(key,"token",s1); + } + + @Override + public PersistentRememberMeToken getTokenForSeries(String s) { + String key = generateKey(s,SERIES_KEY); + List hashKeys = new ArrayList<>(); + hashKeys.add("username"); + hashKeys.add("token"); + hashKeys.add("date"); + List hashValues = redisTemplate.opsForHash().multiGet(key, hashKeys); + String username = hashValues.get(0); + String tokenValue = hashValues.get(1); + String date = hashValues.get(2); + if (null == username || null == tokenValue || null == date) { + return null; + } + Long timestamp = Long.valueOf(date); + Date time = new Date(timestamp); + return new PersistentRememberMeToken(username, s, tokenValue, time); + } + + @Override + public void removeUserTokens(String s) { + //rememberMeService实现类中调用这个方法传入的参数是username,因此我们必须通过username查找到 + //对应的series,然后再通过series查找到对应的token信息再删除。 + String key = generateKey(s,USERNAME_KEY); + deleteIfPresent(key); + } + + private void deleteIfPresent(String key){ + //删除token时应该同时删除token信息,以及保存了对应的username与series对照数据。 + if(redisTemplate.hasKey(key)){ + String series = generateKey(stringRedisTemplate.opsForValue().get(key),SERIES_KEY); + if(series!=null && redisTemplate.hasKey(series)){ + redisTemplate.delete(series); + redisTemplate.delete(key); + } + } + } + private String generateKey(String series,String prefix) { + return prefix + series; + } + +} diff --git a/pear-entrance/src/main/resources/application-datasource.yml b/pear-entrance/src/main/resources/application-datasource.yml index 251134b8cf9b1c3bbcdf2d890bbabd9d6c39b205..f57f93696c5a5131781c8050042a2ec88fb5aef9 100644 --- a/pear-entrance/src/main/resources/application-datasource.yml +++ b/pear-entrance/src/main/resources/application-datasource.yml @@ -1,5 +1,21 @@ spring: + redis: + host: 192.168.0.110 + port: 6379 + password: + database: 0 + lettuce: + pool: + # 连接池中的最大空闲连接 + max-active: 20 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1 + # 连接池中的最大空闲连接 + max-idle: 4 + # 连接池中的最小空闲连接 + min-idle: 0 + timeout: 1000 datasource: dynamic: primary: master #设置默认的数据源或者数据源组,默认值即为master @@ -48,4 +64,4 @@ spring: merge-sql: true wall: config: - multi-statement-allow: true \ No newline at end of file + multi-statement-allow: true diff --git a/pear-entrance/src/main/resources/templates/login.html b/pear-entrance/src/main/resources/templates/login.html index ed93a388bc660aff5fb50c7fd85eb0a721a27e84..ce21dc5ac6bdddf39cc9d3ec88347b86b588dcef 100644 --- a/pear-entrance/src/main/resources/templates/login.html +++ b/pear-entrance/src/main/resources/templates/login.html @@ -75,11 +75,37 @@ document.getElementById("captchaImage").src="/system/captcha/generate?"+Math.random(); },30000) + + validateKickout(); + }) + + diff --git a/pear-modules/pear-system/src/main/java/com/pearadmin/system/controller/SysUserController.java b/pear-modules/pear-system/src/main/java/com/pearadmin/system/controller/SysUserController.java index d63d5a1a14dd53442ad5773c476dffcff3e9b927..2c230feeea2d385bd4b2b2920828935340377495 100644 --- a/pear-modules/pear-system/src/main/java/com/pearadmin/system/controller/SysUserController.java +++ b/pear-modules/pear-system/src/main/java/com/pearadmin/system/controller/SysUserController.java @@ -249,7 +249,7 @@ public class SysUserController extends BaseController { @GetMapping("center") @ApiOperation(value = "个人资料") public ModelAndView center(Model model){ - SysUser sysUser = (SysUser) ServletUtil.getSession().getAttribute("currentUser"); + SysUser sysUser = (SysUser) SecurityUtil.currentUser().getPrincipal(); model.addAttribute("userInfo",sysUserService.getById(sysUser.getUserId())); return JumpPage(MODULE_PATH + "center"); } diff --git a/pear-modules/pear-system/src/main/java/com/pearadmin/system/mapper/SysPowerMapper.java b/pear-modules/pear-system/src/main/java/com/pearadmin/system/mapper/SysPowerMapper.java index 69599de25c45f318a6ee0618b40d497d9f208eb9..36277b20fd950c32db08515faf20519d8bfb3cb2 100644 --- a/pear-modules/pear-system/src/main/java/com/pearadmin/system/mapper/SysPowerMapper.java +++ b/pear-modules/pear-system/src/main/java/com/pearadmin/system/mapper/SysPowerMapper.java @@ -45,12 +45,6 @@ public interface SysPowerMapper { * */ List selectMenuByUsername(String username); - /** - * Describe: 根据 ParentId 查询子菜单 - * Param: parentId - * Return: ResuMenu - * */ - List selectAdminsMenu(); /** * Describe: 修改权限信息 diff --git a/pear-modules/pear-system/src/main/java/com/pearadmin/system/mapper/xml/SysPowerMapper.xml b/pear-modules/pear-system/src/main/java/com/pearadmin/system/mapper/xml/SysPowerMapper.xml index 305f5e05e40e430d878284a152d647440ccd1dc8..eeb505a7ad4e1a6ae60f121b9b7555869715a059 100644 --- a/pear-modules/pear-system/src/main/java/com/pearadmin/system/mapper/xml/SysPowerMapper.xml +++ b/pear-modules/pear-system/src/main/java/com/pearadmin/system/mapper/xml/SysPowerMapper.xml @@ -31,18 +31,14 @@ left join sys_role sr on sr.role_id = srp.role_id left join sys_user_role sur on sur.role_id = sr.role_id left join sys_user su on su.user_id = sur.user_id - where su.username = #{username} and sp.enable = 1 order by sp.sort + + + su.username = #{username} + + and sp.enable = 1 order by sp.sort + - - - +