购买网站模板怎么用,广告设计是学什么的,做设计想接外单去哪个网站好,建立网站的请示解决Springboot整合Shiro自定义SessionDAORedis管理会话#xff0c;登录后不跳转首页 问题发现问题解决 问题发现
在Shiro框架中#xff0c;SessionDAO的默认实现是MemorySessionDAO。它内部维护了一个ConcurrentMap来保存session数据#xff0c;即将session数据缓存在内存… 解决Springboot整合Shiro自定义SessionDAORedis管理会话登录后不跳转首页 问题发现问题解决 问题发现
在Shiro框架中SessionDAO的默认实现是MemorySessionDAO。它内部维护了一个ConcurrentMap来保存session数据即将session数据缓存在内存中。
再使用Redis作为Session存储解决分布式系统中的Session共享问题。
依赖文件如下
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdversion2.7.18/version
/dependency
dependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-spring-boot-web-starter/artifactIdversion1.13.0/version
/dependency示例代码如下
Controller
RequestMapping(value /user)
public class UserController {GetMapping(/index)public ModelAndView index() {Subject subject SecurityUtils.getSubject();System.out.println(index);System.out.println(subject.getSession().getId());System.out.println(subject.isAuthenticated());if (subject.isAuthenticated() || subject.isRemembered()) {return new ModelAndView(redirect:main);}return new ModelAndView(login.html);}PostMapping(/login)public ModelAndView login(HttpServletRequest request, RequestParam(username) String username, RequestParam(password) String password) {// 提前加密解决自定义缓存匹配时错误UsernamePasswordToken token new UsernamePasswordToken(username,//身份信息password);//凭证信息ModelAndView modelAndView new ModelAndView();// 对用户信息进行身份认证Subject subject SecurityUtils.getSubject();if (subject.isAuthenticated() subject.isRemembered()) {modelAndView.setViewName(redirect:main);return modelAndView;}try {subject.login(token);// 判断savedRequest不为空时获取上一次停留页面进行跳转SavedRequest savedRequest WebUtils.getSavedRequest(request);if (savedRequest ! null) {String requestUrl savedRequest.getRequestUrl();modelAndView.setViewName(redirect: requestUrl);return modelAndView;}} catch (AuthenticationException e) {e.printStackTrace();modelAndView.addObject(responseMessage, 用户名或者密码错误);modelAndView.setViewName(redirect:index);return modelAndView;}System.out.println(subject.getSession().getId());System.out.println(subject.isAuthenticated());modelAndView.setViewName(redirect:main);return modelAndView;}GetMapping(/main)public String main() {Subject subject SecurityUtils.getSubject();System.out.println(main);System.out.println(subject.getSession().getId());System.out.println(subject.isAuthenticated());return main.html;}
}自定义SessionDAO示例代码如下
public class RedisSessionDao extends AbstractSessionDAO {private HashOperationsString, Object, Session hashOperations;private static final String key shiro:;public RedisSessionDao(RedisTemplateString, Object redisTemplate) {hashOperations redisTemplate.opsForHash();}Overrideprotected Serializable doCreate(Session session) {Serializable sessionId super.generateSessionId(session);this.assignSessionId(session, sessionId);this.storeSession(sessionId, session);return sessionId;}Overrideprotected Session doReadSession(Serializable serializable) {return (Session) hashOperations.get(key, serializable.toString());}Overridepublic void update(Session session) throws UnknownSessionException {this.storeSession(session.getId(), session);}Overridepublic void delete(Session session) {if (session null) {throw new NullPointerException(session argument cannot be null.);} else {Serializable id session.getId();if (id ! null) {hashOperations.delete(key, id.toString());}}}Overridepublic CollectionSession getActiveSessions() {return hashOperations.values(key);}protected void storeSession(Serializable id, Session session) {if (id null) {throw new NullPointerException(id argument cannot be null.);} else {this.hashOperations.putIfAbsent(key, id.toString(), session);}}
}Config配置文件示例代码如下
Configuration
public class ShiroConfig {/*** 核心安全过滤器对进入应用的请求进行拦截和过滤从而实现认证、授权、会话管理等安全功能。*/Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);// 当未登录的用户尝试访问受保护的资源时重定向到这个指定的登录页面。shiroFilterFactoryBean.setLoginUrl(/user/index);// 成功后跳转地址但是测试时未生效shiroFilterFactoryBean.setSuccessUrl(/user/main);// 当用户访问没有权限的资源时系统重定向到指定的URL地址。MapString, String filterChainDefinitionMap new LinkedHashMap();filterChainDefinitionMap.put(/user/login, anon);filterChainDefinitionMap.put(/**, authc);shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}/*** 创建Shiro Web应用的整体安全管理*/Beanpublic DefaultWebSecurityManager securityManager() {DefaultWebSecurityManager defaultWebSecurityManager new DefaultWebSecurityManager();defaultWebSecurityManager.setRealm(realm());defaultWebSecurityManager.setSessionManager(defaultWebSessionManager()); // 注册会话管理// 可以添加其他配置如缓存管理器、会话管理器等return defaultWebSecurityManager;}/*** 创建会话管理*/Beanpublic DefaultWebSessionManager defaultWebSessionManager() {DefaultWebSessionManager defaultWebSessionManager new DefaultWebSessionManager();defaultWebSessionManager.setGlobalSessionTimeout(10000);defaultWebSessionManager.setSessionDAO(sessionDAO());defaultWebSessionManager.setCacheManager(cacheManager());return defaultWebSessionManager;}Beanpublic SessionDAO sessionDAO() {RedisSessionDao redisSessionDao new RedisSessionDao(redisTemplate());return redisSessionDao;}/*** 指定密码加密算法类型*/Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher new HashedCredentialsMatcher();hashedCredentialsMatcher.setHashAlgorithmName(SHA-256); // 设置哈希算法return hashedCredentialsMatcher;}/*** 注册Realm的对象用于执行安全相关的操作如用户认证、权限查询*/Beanpublic Realm realm() {UserRealm userRealm new UserRealm();userRealm.setCredentialsMatcher(hashedCredentialsMatcher()); // 为realm设置指定算法userRealm.setCachingEnabled(true); // 启动全局缓存userRealm.setAuthorizationCachingEnabled(true); // 启动授权缓存userRealm.setAuthenticationCachingEnabled(true); // 启动验证缓存userRealm.setCacheManager(cacheManager());return userRealm;}Beanpublic CacheManager cacheManager() {RedisCacheManage redisCacheManage new RedisCacheManage(redisTemplate());return redisCacheManage;}Autowiredprivate RedisConnectionFactory redisConnectionFactory;// redis序列化配置Beanpublic RedisTemplateString, Object redisTemplate() {RedisTemplateString, Object redisTemplate new RedisTemplate();redisTemplate.setConnectionFactory(redisConnectionFactory);Jackson2JsonRedisSerializerObject jackson2JsonRedisSerializer new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper new ObjectMapper();//设置了 ObjectMapper 的可见性规则。通过该设置所有字段包括 private、protected 和 package-visible 等都将被序列化和反序列化无论它们的可见性如何。objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);//启用了默认的类型信息 NON_FINAL 参数表示只有非 final 类型的对象才包含类型信息这可以帮助在反序列化时正确地将 JSON 字符串转换回对象。objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);StringRedisSerializer stringRedisSerializer new StringRedisSerializer();redisTemplate.setHashKeySerializer(stringRedisSerializer);redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());return redisTemplate;}
}进入浏览器登陆成功后跳转首页跳转过程中302返回登录页面如图所示
问题解决
根据代码日志可知道跳转到其他页面时Session没有共享如图所示 最开始以为Redis中没有保存记录其实已经保存了如图所示 参考网上诸多案例似乎没什么区别也不知道他们测过没有。
然后再Debug的时候发现了另外一个类EnterpriseCacheSessionDAO于是参考该类我就把对应代码继承CachingSessionDAO示例代码如下
public class RedisSessionDao extends CachingSessionDAO {private HashOperationsString, Object, Session hashOperations;protected Serializable doCreate(Session session) {Serializable sessionId this.generateSessionId(session);this.assignSessionId(session, sessionId);return sessionId;}protected Session doReadSession(Serializable sessionId) {return null;}protected void doUpdate(Session session) {}protected void doDelete(Session session) {}
}Config配置文件示例代码如下 Beanpublic DefaultWebSessionManager defaultWebSessionManager() {DefaultWebSessionManager defaultWebSessionManager new DefaultWebSessionManager();defaultWebSessionManager.setGlobalSessionTimeout(10000);defaultWebSessionManager.setSessionDAO(sessionDAO());defaultWebSessionManager.setCacheManager(cacheManager());return defaultWebSessionManager;}Beanpublic SessionDAO sessionDAO() {RedisSessionDao redisSessionDao new RedisSessionDao();redisSessionDao.setCacheManager(cacheManager()); // 设置缓存管理器redisSessionDao.setActiveSessionsCacheName(shiro:session); // 自定义redis存放的key名称return redisSessionDao;}重启项目后运行成功跳转如图所示 Redis中也有记录如图所示 至于继承AbstractSessionDAO为什么没有共享Session大概率的原因是Redis没有被Shiro给管理导致的。
示例代码如下
public class RedisSessionDao extends AbstractSessionDAO {private CacheManager cacheManager;private CacheSerializable, Session activeSessions;private static final String key shiro:;public RedisSessionDao() {}public void setCacheManager(CacheManager cacheManager) {this.cacheManager cacheManager;this.activeSessions cacheManager.getCache(key);}Overrideprotected Serializable doCreate(Session session) {Serializable sessionId super.generateSessionId(session);this.assignSessionId(session, sessionId);this.storeSession(sessionId, session);return sessionId;}Overrideprotected Session doReadSession(Serializable serializable) {return (Session) activeSessions.get(serializable);}Overridepublic void update(Session session) throws UnknownSessionException {this.storeSession(session.getId(), session);}Overridepublic void delete(Session session) {if (session null) {throw new NullPointerException(session argument cannot be null.);} else {Serializable id session.getId();if (id ! null) {activeSessions.remove(id);}}}Overridepublic CollectionSession getActiveSessions() {return activeSessions.values();}protected void storeSession(Serializable id, Session session) {if (id null) {throw new NullPointerException(id argument cannot be null.);} else {activeSessions.put(id, session);}}
}配置文件示例代码如下 /*** 创建会话管理*/Beanpublic DefaultWebSessionManager defaultWebSessionManager() {DefaultWebSessionManager defaultWebSessionManager new DefaultWebSessionManager();defaultWebSessionManager.setGlobalSessionTimeout(10000);defaultWebSessionManager.setSessionDAO(sessionDAO());defaultWebSessionManager.setCacheManager(cacheManager());return defaultWebSessionManager;}Beanpublic SessionDAO sessionDAO() {RedisSessionDao redisSessionDao new RedisSessionDao();redisSessionDao.setCacheManager(cacheManager()); // 设置缓存管理器return redisSessionDao;}经过测试也是可以成功跳转会话共享。