diff options
| author | shizhendong <[email protected]> | 2023-06-27 10:59:03 +0800 |
|---|---|---|
| committer | shizhendong <[email protected]> | 2023-06-27 10:59:03 +0800 |
| commit | 81b3af82a0df352e3969b015dd4dd22bde0a005c (patch) | |
| tree | 0122db036c8646d2677bd6bcd3471cd942aa5e27 | |
| parent | a69a38c9396d070939168ee50b2588685385f19b (diff) | |
fix: NEZ-2918 shiro-redis 通过 redisTemplate 操作 redis
3 files changed, 421 insertions, 53 deletions
diff --git a/nz-admin/src/main/java/com/nis/common/config/ShiroConfig.java b/nz-admin/src/main/java/com/nis/common/config/ShiroConfig.java index 5c94a31b..0853bb5b 100644 --- a/nz-admin/src/main/java/com/nis/common/config/ShiroConfig.java +++ b/nz-admin/src/main/java/com/nis/common/config/ShiroConfig.java @@ -1,13 +1,18 @@ package com.nis.common.config; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.servlet.Filter; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletResponse; - +import Aladdin.HaspStatus; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.nis.common.interceptor.TokenCheckFilter; +import com.nis.common.utils.*; +import com.nis.modules.metric.dto.NezhaMetrics; +import com.nis.modules.sys.dao.SysApiKeyDao; +import com.nis.modules.sys.dao.SysUserDao; +import com.nis.modules.sys.entity.SysConfigEntity; +import com.nis.modules.sys.service.LicenseService; +import com.nis.modules.sys.service.SysConfigService; +import com.nis.modules.sys.shiro.*; +import io.micrometer.core.instrument.MeterRegistry; import org.apache.catalina.connector.Connector; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; @@ -16,7 +21,6 @@ import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.filter.AccessControlFilter; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.crazycake.shiro.RedisCacheManager; -import org.crazycake.shiro.RedisManager; import org.crazycake.shiro.RedisSessionDAO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -27,66 +31,55 @@ import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerF import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; +import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.nis.common.interceptor.TokenCheckFilter; -import com.nis.common.utils.Constant; -import com.nis.common.utils.R; -import com.nis.common.utils.RCode; -import com.nis.common.utils.Tool; -import com.nis.common.utils.ToolUtil; -import com.nis.modules.metric.dto.NezhaMetrics; -import com.nis.modules.sys.dao.SysApiKeyDao; -import com.nis.modules.sys.dao.SysUserDao; -import com.nis.modules.sys.entity.SysConfigEntity; -import com.nis.modules.sys.service.LicenseService; -import com.nis.modules.sys.service.SysConfigService; -import com.nis.modules.sys.shiro.MySessionManager; -import com.nis.modules.sys.shiro.ShiroUtils; -import com.nis.modules.sys.shiro.UserRealm; - -import Aladdin.HaspStatus; -import cn.hutool.core.util.StrUtil; -import io.micrometer.core.instrument.MeterRegistry; +import javax.servlet.Filter; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; +import java.util.LinkedHashMap; +import java.util.Map; /** * Shiro的配置文件 - * */ @Configuration @DependsOn(value = { "mybatisPlusConfig", "redisConfig" }) public class ShiroConfig { + @Value("${compileType:release}") + private String compileType; + + @Value("${vendorCode}") + private String vendorCode; + @Autowired - private RedisConfig config; + private LicenseService licenseService; @Autowired - private RedisTemplate<String, String> redisTemplate; + private SysConfigService sysconfigService; @Autowired private SysUserDao sysUserDao; @Autowired - private SysConfigService sysconfigService; + private SysApiKeyDao sysApiKeyDao; @Autowired - private SysApiKeyDao sysApiKeyDao; - - @Value("${compileType:release}") - private String compileType; - @Value("${vendorCode}") - private String vendorCode; + private NezhaMetrics nezhaMetrics; + @Autowired - private NezhaMetrics nezhaMetrics; + private MeterRegistry meterRegistry; - @Autowired - private MeterRegistry meterRegistry; @Autowired - private LicenseService licenseService; - + private RedisTemplate<String, String> redisTemplate; + + @Autowired + private RedisConnectionFactory redisConnectionFactory; + @Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); @@ -220,14 +213,8 @@ public class ShiroConfig { * * @return */ - public RedisManager redisManager() { - RedisManager redisManager = new RedisManager(); - redisManager.setHost(config.getRedisHost() + ":" + config.getRedisPort()); - redisManager.setTimeout(Tool.NumberUtil.parseInt(Tool.StrUtil.toString(config.getRedisTimeout()))); - redisManager.setDatabase(config.getRedisDatabase()); - if (Tool.StrUtil.isNotBlank(config.getRedisPin())) { - redisManager.setPassword(config.getRedisPin()); - } + public MyRedisManager redisManager() { + MyRedisManager redisManager = new MyRedisManager(redisConnectionFactory); return redisManager; } @@ -255,7 +242,7 @@ public class ShiroConfig { @Bean public RedisSessionDAO redisSessionDAO() { Integer loginExpiration = getTimeOutData(); - RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); + MyRedisSessionDAO redisSessionDAO = new MyRedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); redisSessionDAO.setExpire(loginExpiration * 60); redisSessionDAO.setKeyPrefix(ShiroUtils.REDIS_KEYPREFIX); diff --git a/nz-admin/src/main/java/com/nis/modules/sys/shiro/MyRedisManager.java b/nz-admin/src/main/java/com/nis/modules/sys/shiro/MyRedisManager.java new file mode 100644 index 00000000..98271bf5 --- /dev/null +++ b/nz-admin/src/main/java/com/nis/modules/sys/shiro/MyRedisManager.java @@ -0,0 +1,76 @@ +package com.nis.modules.sys.shiro; + +import com.nis.common.utils.Tool; +import org.apache.shiro.session.Session; +import org.crazycake.shiro.IRedisManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * 自定义 redis manager + * + * @author ThinkPad + */ +public class MyRedisManager implements IRedisManager { + + private RedisTemplate<String, Object> redisTemplate; + + public MyRedisManager(RedisConnectionFactory factory) { + redisTemplate = new RedisTemplate<>(); + + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); + + redisTemplate.setConnectionFactory(factory); + redisTemplate.afterPropertiesSet(); + } + + @Override + public byte[] get(byte[] key) { + return null; + } + + @Override + public byte[] set(byte[] key, byte[] value, int expire) { + return null; + } + + @Override + public void del(byte[] key) { + } + + @Override + public Long dbSize(byte[] pattern) { + return 16L; + } + + @Override + public Set<byte[]> keys(byte[] pattern) { + return Tool.CollUtil.newHashSet(); + } + + public Object get(String key) { + ValueOperations<String, Object> valueOperations = this.redisTemplate.opsForValue(); + return valueOperations.get(key); + } + + public Session set(String key, Session value, int expire) { + ValueOperations<String, Object> opsForValue = this.redisTemplate.opsForValue(); + opsForValue.set(key, value, expire, TimeUnit.SECONDS); + return value; + } + + public void del(String key) { + this.redisTemplate.delete(key); + } + + public Set<String> keys(String pattern) { + return this.redisTemplate.keys(pattern); + } +} diff --git a/nz-admin/src/main/java/com/nis/modules/sys/shiro/MyRedisSessionDAO.java b/nz-admin/src/main/java/com/nis/modules/sys/shiro/MyRedisSessionDAO.java new file mode 100644 index 00000000..8fedc6aa --- /dev/null +++ b/nz-admin/src/main/java/com/nis/modules/sys/shiro/MyRedisSessionDAO.java @@ -0,0 +1,305 @@ +package com.nis.modules.sys.shiro; + +import cn.hutool.log.Log; +import org.apache.shiro.session.Session; +import org.apache.shiro.session.UnknownSessionException; +import org.apache.shiro.session.mgt.eis.SessionDAO; +import org.crazycake.shiro.IRedisManager; +import org.crazycake.shiro.RedisSessionDAO; +import org.crazycake.shiro.common.SessionInMemory; +import org.springframework.data.redis.serializer.SerializationException; + +import java.io.Serializable; +import java.util.*; + +public class MyRedisSessionDAO extends RedisSessionDAO implements SessionDAO { + + private static final Log logger = Log.get(); + + private static final String DEFAULT_SESSION_KEY_PREFIX = "shiro:session:"; + private String keyPrefix = "shiro:session:"; + private static final long DEFAULT_SESSION_IN_MEMORY_TIMEOUT = 1000L; + private long sessionInMemoryTimeout = 1000L; + private static final boolean DEFAULT_SESSION_IN_MEMORY_ENABLED = true; + private boolean sessionInMemoryEnabled = true; + private static ThreadLocal sessionsInThread = new ThreadLocal(); + private static final int DEFAULT_EXPIRE = -2; + private static final int NO_EXPIRE = -1; + private int expire = -2; + private static final int MILLISECONDS_IN_A_SECOND = 1000; + private MyRedisManager redisManager; + + public MyRedisSessionDAO() { + } + + @Override + public void update(Session session) throws UnknownSessionException { + if (this.sessionInMemoryEnabled) { + this.removeExpiredSessionInMemory(); + } + + this.saveSession(session); + if (this.sessionInMemoryEnabled) { + this.setSessionToThreadLocal(session.getId(), session); + } + + } + + private void saveSession(Session session) throws UnknownSessionException { + if (session != null && session.getId() != null) { + String key; + Session value; + try { + key = this.getRedisSessionKey(session.getId()); + value = session; + } catch (SerializationException var5) { + logger.error("serialize session error. session id=" + session.getId()); + throw new UnknownSessionException(var5); + } + + if (this.expire == -2) { + this.redisManager.set(key, value, (int) (session.getTimeout() / 1000L)); + } else { + if (this.expire != -1 && (long) (this.expire * 1000) < session.getTimeout()) { + logger.warn("Redis session expire time: " + this.expire * 1000 + " is less than Session timeout: " + session.getTimeout() + " . It may cause some problems."); + } + + this.redisManager.set(key, value, this.expire); + } + } else { + logger.error("session or session id is null"); + throw new UnknownSessionException("session or session id is null"); + } + } + + @Override + public void delete(Session session) { + if (this.sessionInMemoryEnabled) { + this.removeExpiredSessionInMemory(); + } + + if (session != null && session.getId() != null) { + if (this.sessionInMemoryEnabled) { + this.delSessionFromThreadLocal(session.getId()); + } + + try { + this.redisManager.del(this.getRedisSessionKey(session.getId())); + } catch (SerializationException var3) { + logger.error("delete session error. session id=" + session.getId()); + } + + } else { + logger.error("session or session id is null"); + } + } + + @Override + public Collection<Session> getActiveSessions() { + if (this.sessionInMemoryEnabled) { + this.removeExpiredSessionInMemory(); + } + + HashSet sessions = new HashSet(); + + try { + Set<String> keys = this.redisManager.keys(this.keyPrefix + "*"); + if (keys != null && keys.size() > 0) { + Iterator<String> var3 = keys.iterator(); + while (var3.hasNext()) { + String key = var3.next(); + Session s = (Session) this.redisManager.get(key); + sessions.add(s); + } + } + } catch (SerializationException var6) { + logger.error("get active sessions error."); + } + + return sessions; + } + + @Override + protected Serializable doCreate(Session session) { + if (this.sessionInMemoryEnabled) { + this.removeExpiredSessionInMemory(); + } + + if (session == null) { + logger.error("session is null"); + throw new UnknownSessionException("session is null"); + } else { + Serializable sessionId = this.generateSessionId(session); + this.assignSessionId(session, sessionId); + this.saveSession(session); + return sessionId; + } + } + + @Override + protected Session doReadSession(Serializable sessionId) { + if (this.sessionInMemoryEnabled) { + this.removeExpiredSessionInMemory(); + } + + if (sessionId == null) { + logger.warn("session id is null"); + return null; + } else { + Session session; + if (this.sessionInMemoryEnabled) { + session = this.getSessionFromThreadLocal(sessionId); + if (session != null) { + return session; + } + } + + session = null; + + try { + String sessionRedisKey = this.getRedisSessionKey(sessionId); + logger.debug("read session: " + sessionRedisKey + " from Redis"); + session = (Session) this.redisManager.get(sessionRedisKey); + if (this.sessionInMemoryEnabled) { + this.setSessionToThreadLocal(sessionId, session); + } + } catch (SerializationException var4) { + logger.error("read session error. sessionId: " + sessionId); + } + + return session; + } + } + + private void setSessionToThreadLocal(Serializable sessionId, Session session) { + this.initSessionsInThread(); + Map<Serializable, SessionInMemory> sessionMap = (Map) sessionsInThread.get(); + sessionMap.put(sessionId, this.createSessionInMemory(session)); + } + + private void delSessionFromThreadLocal(Serializable sessionId) { + Map<Serializable, SessionInMemory> sessionMap = (Map) sessionsInThread.get(); + if (sessionMap != null) { + sessionMap.remove(sessionId); + } + } + + private SessionInMemory createSessionInMemory(Session session) { + SessionInMemory sessionInMemory = new SessionInMemory(); + sessionInMemory.setCreateTime(new Date()); + sessionInMemory.setSession(session); + return sessionInMemory; + } + + private void initSessionsInThread() { + Map<Serializable, SessionInMemory> sessionMap = (Map) sessionsInThread.get(); + if (sessionMap == null) { + sessionMap = new HashMap(); + sessionsInThread.set(sessionMap); + } + + } + + private void removeExpiredSessionInMemory() { + Map<Serializable, SessionInMemory> sessionMap = (Map) sessionsInThread.get(); + if (sessionMap != null) { + Iterator it = sessionMap.keySet().iterator(); + + while (it.hasNext()) { + Serializable sessionId = (Serializable) it.next(); + SessionInMemory sessionInMemory = (SessionInMemory) sessionMap.get(sessionId); + if (sessionInMemory == null) { + it.remove(); + } else { + long liveTime = this.getSessionInMemoryLiveTime(sessionInMemory); + if (liveTime > this.sessionInMemoryTimeout) { + it.remove(); + } + } + } + + if (sessionMap.size() == 0) { + sessionsInThread.remove(); + } + + } + } + + private Session getSessionFromThreadLocal(Serializable sessionId) { + if (sessionsInThread.get() == null) { + return null; + } else { + Map<Serializable, SessionInMemory> sessionMap = (Map) sessionsInThread.get(); + SessionInMemory sessionInMemory = (SessionInMemory) sessionMap.get(sessionId); + if (sessionInMemory == null) { + return null; + } else { + logger.debug("read session from memory"); + return sessionInMemory.getSession(); + } + } + } + + private long getSessionInMemoryLiveTime(SessionInMemory sessionInMemory) { + Date now = new Date(); + return now.getTime() - sessionInMemory.getCreateTime().getTime(); + } + + private String getRedisSessionKey(Serializable sessionId) { + return this.keyPrefix + sessionId; + } + + @Override + public IRedisManager getRedisManager() { + return this.redisManager; + } + + public void setRedisManager(MyRedisManager redisManager) { + this.redisManager = redisManager; + } + + @Override + public String getKeyPrefix() { + return this.keyPrefix; + } + + @Override + public void setKeyPrefix(String keyPrefix) { + this.keyPrefix = keyPrefix; + } + + + @Override + public long getSessionInMemoryTimeout() { + return this.sessionInMemoryTimeout; + } + + @Override + public void setSessionInMemoryTimeout(long sessionInMemoryTimeout) { + this.sessionInMemoryTimeout = sessionInMemoryTimeout; + } + + @Override + public int getExpire() { + return this.expire; + } + + @Override + public void setExpire(int expire) { + this.expire = expire; + } + + @Override + public boolean getSessionInMemoryEnabled() { + return this.sessionInMemoryEnabled; + } + + @Override + public void setSessionInMemoryEnabled(boolean sessionInMemoryEnabled) { + this.sessionInMemoryEnabled = sessionInMemoryEnabled; + } + + public static ThreadLocal getSessionsInThread() { + return sessionsInThread; + } +} |
