【Java从零到架构师第③季】【49】会话管理—Token_ehcache
持续学习&持续更新中…
守破离
【Java从零到架构师第③季】【49】会话管理—Token_ehcache
- 基于Cookie、Session
- 基于Token
- ehcache
- 简单使用
- 项目使用:登录、登出
- 补充:简单的分布式架构介绍
- 参考
基于Cookie、Session
基于Token
ehcache
简单使用
依赖:
<!-- 缓存 --><dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId></dependency>
标签:
<!-- <persistence directory="F:/ehcache"/> --><cache alias="test"><expiry><!-- none:永不过期 --><none/><!-- tti:time to idle(空闲时间)=> 10月1号登录,默认10月8号就会失效。但是,如果10月3号登陆了,那么10月10号才失效;如果10月4号又登录了,那么再延长7天,10月11号才失效。也就是说,只要7天内不访问,就失效。 --><!-- <tti unit="days">7</tti> --><!-- ttl:time to live(存活时间)=> 10月1号登录,10月8号就失效 --><!-- <ttl unit="days">7</ttl> --></expiry><resources><!-- <offheap unit="MB"/> 堆外内存 --><!-- <disk unit="MB" persistent="true" /> 磁盘 需要搭配persistence标签 --><heap unit="entries">1000</heap> <!-- 放到堆内内存,堆内内存就是new的对象存放的地方 --><!-- <heap>1000</heap> unit="entries" 缓存个数 是默认的 --><!-- <heap unit="MB">1000</heap> 缓存大小:GB、MB、KB、... --></resources></cache>
使用:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'xmlns='http://www.ehcache.org/v3'xsi:schemaLocation="http://www.ehcache.org/v3http://www.ehcache.org/schema/ehcache-core.xsd"><cache-template name="common"><key-type>java.lang.Object</key-type><value-type>java.lang.Object</value-type><resources><heap>10000</heap></resources></cache-template><!--11月1号~11月7号,11月8号过期11月6号~11月12号,11月13号过期11月12号~11月18号,11月19号过期--><!-- 存放token的缓存:只要7天内不访问,就失效 --><cache alias="token" uses-template="common"><expiry><tti unit="days">7</tti></expiry></cache><!-- 默认缓存:永不过期 --><cache alias="default" uses-template="common"><expiry><none/></expiry></cache>
</config>
public class Caches {private static final CacheManager MGR;private static final Cache<Object, Object> DEFAULT_CACHE;private static final Cache<Object, Object> TOKEN_CACHE;static {// 初始化缓存管理器URL url = Caches.class.getClassLoader().getResource("ehcache.xml");assert url != null;Configuration cfg = new XmlConfiguration(url);MGR = CacheManagerBuilder.newCacheManager(cfg);MGR.init();// 缓存对象DEFAULT_CACHE = MGR.getCache("default", Object.class, Object.class);TOKEN_CACHE = MGR.getCache("token", Object.class, Object.class);}public static void put(Object key, Object value) {if (key == null || value == null) return;DEFAULT_CACHE.put(key, value);}public static void remove(Object key) {if (key == null) return;DEFAULT_CACHE.remove(key);}public static <T> T get(Object key) {if (key == null) return null;return (T) DEFAULT_CACHE.get(key);}public static void clear() {DEFAULT_CACHE.clear();}public static void putToken(Object key, Object value) {if (key == null || value == null) return;TOKEN_CACHE.put(key, value);}public static void removeToken(Object key) {if (key == null) return;TOKEN_CACHE.remove(key);}public static <T> T getToken(Object key) {if (key == null) return null;return (T) TOKEN_CACHE.get(key);}public static void clearToken() {TOKEN_CACHE.clear();}
}
项目使用:登录、登出
- 只要近7天登录过,就不会失效,就不用重新登录
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'xmlns='http://www.ehcache.org/v3'xsi:schemaLocation="http://www.ehcache.org/v3http://www.ehcache.org/schema/ehcache-core.xsd"><cache-template name="common"><key-type>java.lang.Object</key-type><value-type>java.lang.Object</value-type><resources><heap>10000</heap></resources></cache-template><!--11月1号~11月7号,11月8号过期11月6号~11月12号,11月13号过期11月12号~11月18号,11月19号过期--><!-- 存放token的缓存:只要7天内不访问,就失效 --><cache alias="token" uses-template="common"><expiry><tti unit="days">7</tti>
<!-- <tti unit="seconds">7</tti>--></expiry><listeners><listener><class>programmer.lp.jk.common.cache.TokenCacheListener</class><!-- 异步回调 --><event-firing-mode>ASYNCHRONOUS</event-firing-mode><!-- 不用按顺序处理事件 --><event-ordering-mode>UNORDERED</event-ordering-mode><!-- 哪些操作会触发监听器:添加、过期、删除 --><events-to-fire-on>CREATED</events-to-fire-on><events-to-fire-on>EXPIRED</events-to-fire-on><events-to-fire-on>REMOVED</events-to-fire-on></listener></listeners></cache><!-- 默认缓存:永不过期 --><cache alias="default" uses-template="common"><expiry><none/></expiry></cache>
</config>
public class TokenCacheListener implements CacheEventListener<Object, Object> {@Overridepublic void onEvent(CacheEvent cacheEvent) {String token = (String) cacheEvent.getKey();switch (cacheEvent.getType()) {case CREATED: {// 添加了一个新的token(说明有一个用户刚登录)SysUserDto user = (SysUserDto) cacheEvent.getNewValue();// 以便将来通过用户id找到他对应的tokenCaches.put(user.getUser().getId(), token);break;}case EXPIRED:case REMOVED: { // token被移除或者过期了SysUserDto user = (SysUserDto) cacheEvent.getOldValue();Caches.remove(user.getUser().getId());break;}default: break;}}
}
@Overridepublic RespLogin login(ReqLogin reqVo) {// 根据用户名查询用户MPLambdaQueryWrapper<SysUser> wrapper = new MPLambdaQueryWrapper<>();wrapper.eq(SysUser::getUsername, reqVo.getUsername());SysUser po = baseMapper.selectOne(wrapper);// 用户名不存在if (po == null) {return JSONResults.exception(CodeMsg.WRONG_USERNAME);}// 密码不正确if (!po.getPassword().equals(reqVo.getPassword())) {return JSONResults.exception(CodeMsg.WRONG_PASSWORD);}// 账号锁定if (po.getStatus() == Constants.SysUserStatus.LOCKED) {return JSONResults.exception(CodeMsg.USER_LOCKED);}/** 登录成功 **/// 更新登录时间po.setLoginTime(new Date());baseMapper.updateById(po);SysUserDto dto = new SysUserDto();dto.setUser(po);// 根据用户id查询所有的角色:sys_role,sys_user_roleList<SysRole> roles = roleService.listByUserId(po.getId());// 根据角色id查询所有的资源:sys_resource、sys_role_resourceif (!CollectionUtils.isEmpty(roles)) {dto.setRoles(roles);List<Short> roleIds = Streams.map(roles, SysRole::getId);List<SysResource> resources = resourceService.listByRoleIds(roleIds);dto.setResources(resources);}// 生成Token,发送Token给用户String token = UUID.randomUUID().toString();// 存储token到缓存中Caches.putToken(token, dto);// 返回给客户端的具体数据RespLogin vo = MapStruct.INSTANCE.po2loginVo(po);vo.setToken(token);return vo;}
@PostMapping("/logout")@ApiOperation("退出登录")public JSONResult logout(@RequestHeader(TokenFilter.HEADER_TOKEN) String token) {Caches.removeToken(token);return JSONResults.ok(CodeMsg.LOGOUT_OK);}
@Overridepublic boolean saveOrUpdate(ReqSaveSysUser entity) {// 转成POSysUser po = MapStruct.INSTANCE.vo2po(entity);// 保存用户信息if (!saveOrUpdate(po)) return false;Integer id = entity.getId();if (id != null && id > 0) {// 如果是做更新// 将更新成功的用户从缓存中移除(让token失效,用户必须重新登录)Caches.removeToken(Caches.get(id));
// Caches.remove(id);// 删除当前用户的所有角色信息userRoleService.removeByUserId(entity.getId());}// 保存角色信息String roleIdsStr = entity.getRoleIds();if (Strings.isEmpty(roleIdsStr)) return true;String[] roleIds = roleIdsStr.split(",");List<SysUserRole> userRoles = new ArrayList<>();Integer userId = po.getId();for (String roleId : roleIds) { // 构建SysUserRole对象SysUserRole userRole = new SysUserRole();userRole.setUserId(userId);userRole.setRoleId(Short.parseShort(roleId));userRoles.add(userRole);}return userRoleService.saveBatch(userRoles);}
// 角色信息被更新了也要让用户重新登录.
// 重新登录是为了去查用户的权限(资源),让其配合Shiro使用@Overridepublic boolean saveOrUpdate(ReqSaveSysRole entity) {// 转成POSysRole po = MapStruct.INSTANCE.vo2po(entity);// 保存角色信息if (!saveOrUpdate(po)) return false;Short id = entity.getId();if (id != null && id > 0) {MPLambdaQueryWrapper<SysUserRole> wrapper = new MPLambdaQueryWrapper<>();wrapper.select(SysUserRole::getUserId);wrapper.eq(SysUserRole::getRoleId, id);List<Object> userIds = userRoleMapper.selectObjs(wrapper);if (!CollectionUtils.isEmpty(userIds)) {for (Object userId : userIds) {// 将拥有这个角色的用户从缓存中移除(让token失效,用户必须重新登录)Caches.removeToken(Caches.get(userId));}}// 删除当前角色的所有资源信息roleResourceService.removeByRoleId(id);}// 保存角色信息String resourceIdsStr = entity.getResourceIds();if (Strings.isEmpty(resourceIdsStr)) return true;String[] resourceIds = resourceIdsStr.split(",");List<SysRoleResource> roleResources = new ArrayList<>();Short roleId = po.getId();for (String resourceId : resourceIds) { // 构建SysUserRole对象SysRoleResource roleResource = new SysRoleResource();roleResource.setRoleId(roleId);roleResource.setResourceId(Short.parseShort(resourceId));roleResources.add(roleResource);}return roleResourceService.saveBatch(roleResources);}
补充:简单的分布式架构介绍
参考
小码哥-李明杰: Java从0到架构师③进阶互联网架构师.
本文完,感谢您的关注支持!
【Java从零到架构师第③季】【49】会话管理—Token_ehcache相关推荐
- 【Java从零到架构师第③季】【48】SpringBoot-Swagger
持续学习&持续更新中- 守破离 [Java从零到架构师第③季][48]SpringBoot-Swagger 接口文档-Swagger 基本使用 不使用starter 使用starter(Swa ...
- 【Java从零到架构师第③季】【26】SpringMVC-反射获取方法参数名_SpringMVC是如何获取方法的参数名的
持续学习&持续更新中- 守破离 [Java从零到架构师第③季][26]SpringMVC-反射获取方法参数名_SpringMVC是如何获取方法的参数名的 利用反射获取方法的参数名 直接编译 修 ...
- 【Java从零到架构师第③季】【24】SpringMVC-概述_入门
持续学习&持续更新中- 守破离 [Java从零到架构师第③季][24]SpringMVC-概述_入门 Spring.SpringMVC.MyBatis之间的关系 SpringMVC简介 Spr ...
- 【Java从零到架构师第③季】【28】SpringMVC-Servlet的URL匹配_path-matching suffix-pattern
持续学习&持续更新中- 守破离 [Java从零到架构师第③季][28]SpringMVC-Servlet的URL匹配_path-matching suffix-pattern Servlet的 ...
- 【Java从零到架构师第二季】【07】JDBC FOR MySQL
持续学习&持续更新中- 学习态度:守破离 JDBC FOR MySQL 什么是JDBC 如何通过Java操作数据库 JDBC是属于JavaSE的一部分 下载MySQL的JDBC实现 JDBC细 ...
- 【Java从零到架构师第二季】【14】AJAX
持续学习&持续更新中- 学习态度:守破离 AJAX 同步请求和异步请求 未学AJAX之前向服务器提交请求的方式 同步和异步 AJAX 什么是AJAX AJAX的常见使用方式 原生 jQuery ...
- 【Java从0到架构师】项目实战 - 会话管理、EhCache、JWT、权限管理 Shiro、打包部署
项目实战 - 权限管理 会话管理 客户端身份认证 - 基于 Cookie.Session 客户端身份验证 - 基于 token EhCache - 简单的缓存框架 JWT - 基于 JSON 的 to ...
- 【Java从零到架构师第1季】【并发 Concurrent 03】线程间通信_ReentrantLock_线程池
持续学习&持续更新中- 守破离 [Java从零到架构师第1季][并发 Concurrent 03]线程间通信_ReentrantLock_线程池 线程间通信 线程间通信-示例 可重入锁Reen ...
- 【Java从0到架构师】Linux 应用 - 软件包管理、软件安装
Linux 应用 - 软件包管理.软件安装 软件包管理器 rpm yum 软件的安装 jdk 1.8 mysql 5.7 tomcat8 Java 从 0 到架构师目录:[Java从0到架构师]学习记 ...
最新文章
- JavaScript中的正则表达式解析
- 用switch写收水费的c语言程序,超级新手,用switch写了个计算器程序,求指导
- 互联网协议 — OSPF 开放式最短路径优先协议
- Linux 6.8 root密码丢失找回
- SQL SERVER 常用日期计算
- Oracle 备份还原
- qt 关闭对话框 提醒_qt5信息提示框QMessageBox用法
- 数据库经典文章!(必备)
- Could not load the Tomcat server configuration at \Servers\Tomcat v7.0 Server at localhost-config
- C#操作SQLite数据库
- OpenShift 4 之Service Mesh教程(2)- 用Kiali监控微服务运行
- 开课吧Java课堂:什么是线程优先级?
- /usr/bin/ld: 找不到 -lglut
- ghost for linux 教程,今天解决了ghost for linux的问题
- 你们要的最小样本量计算来了。
- JAVA里面一加到一百等于多少_从1加到100等于多少 【求和算法汇总】
- Java Instrument实践应用:运行中修改程序的Class
- 高仿淘宝首页 - 刚把CSS和JS弄出成了外部,原本写的时候都在HTML一个文件里哈
- Altium Designer 3D元件库,PCB封装库,极为全面一份足以
- 读书感受 之 《冰与火之歌》
热门文章
- 通过google插件Thumbnails实现图片指定大小压缩
- 彻底卸载并重装Anaconda环境与Python的方法
- 简述计算机用二进制的原因,简述计算机采用二进制的原因
- register_chrdev_region、alloc_chrdev_region、register_chrdev区别
- ArrayDeque集合的妙用
- Presto 在字节跳动的应用
- 2019计算机小高考成绩,小高考没过怎么办 2021小高考难度如何
- 代价函数,损失函数,目标函数区别
- 用python做网站开发的课程_腾讯课堂:Flask Python Web 网站开发
- 卫星遥感加持,农业更高效精准