当前位置: 首页 > news >正文

天河区网站建设网站注册地址查询

天河区网站建设,网站注册地址查询,网站建设速成班,潍坊专业网站建设最新报价解决Springboot整合Shiro自定义SessionDAORedis管理会话,登录后不跳转首页 问题发现问题解决 问题发现 在Shiro框架中,SessionDAO的默认实现是MemorySessionDAO。它内部维护了一个ConcurrentMap来保存session数据,即将session数据缓存在内存…

解决Springboot整合Shiro自定义SessionDAO+Redis管理会话,登录后不跳转首页

  • 问题发现
  • 问题解决

问题发现

Shiro框架中,SessionDAO的默认实现是MemorySessionDAO。它内部维护了一个ConcurrentMap来保存session数据,即将session数据缓存在内存中。

再使用Redis作为Session存储解决分布式系统中的Session共享问题。

依赖文件如下:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.18</version>
</dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-web-starter</artifactId><version>1.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 HashOperations<String, Object, Session> hashOperations;private static final String key = "shiro:";public RedisSessionDao(RedisTemplate<String, 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 Collection<Session> 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地址。Map<String, 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 RedisTemplate<String, Object> redisTemplate() {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactory);Jackson2JsonRedisSerializer<Object> 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 HashOperations<String, 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 Cache<Serializable, 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 Collection<Session> 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;}

经过测试也是可以成功跳转,会话共享。

在这里插入图片描述

http://www.mmbaike.com/news/36162.html

相关文章:

  • 搭理彩票网站开发推广公司运营模式
  • 餐饮加盟网站模板北京网站建设公司报价
  • 张家口全景网站建设中国网络营销网
  • 做网站第一步要学什么苏州网站seo优化
  • 个体工商户可以做网站备案吗百度网盘会员
  • 替朋友做网站在线网页服务器
  • 为什么做的网站搜不出来的郑州网站关键词推广
  • java做网站怎么验证登录如何设计网站步骤
  • 怎么识别网站是用什么语言做的人工智能培训机构排名
  • 免费给人做网站的技能培训网
  • 上海哪个区最繁华美国seo薪酬
  • 网站品牌建设功能优化关键词有哪些方法
  • 信息发布型企业网站的特点怎么样推广最有效最快速
  • 网站原创页面厦门百度整站优化服务
  • 网页设计论文前言seo优化服务是什么
  • 贵阳中企动力做的网站谷歌广告上海有限公司官网
  • 做付费网站最新旅游热点
  • 承接做网站南京网站推广排名
  • 做农业种子的网站广告软文外链平台
  • 做网站前端设计需要哪些证书网页设计模板网站免费
  • 可信验证网站西安网站seo价格
  • 网站兼容浏览器服务郑州seo外包费用
  • WordPress唯美个人博客主题seo网站诊断方案
  • 兰州商城网站建设互联网的推广
  • 马鞍山网站建设兼职服务外包公司
  • 域名通过了才可以做网站吗网页制作培训网站
  • B2B网站建设哪家好营销型网站建设报价
  • 专业的设计网站有哪些市场推广计划怎么写
  • 专门做衬衣网站百度pc版网页
  • WordPress立体边框福州seo建站