今天给大家分享session缓存配置与session持久化示例
在权限验证时每次从数据库中获取登陆权限数据显然是不合适的,更合适方式是将数据缓存到内存,以提高系统性能。
-
- <dependency>
- <groupId>org.apache.shirogroupId>
- <artifactId>shiro-ehcacheartifactId>
- <version>${shiro-version}version>
- dependency>
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-context-supportartifactId>
- <version>${spring-version}version>
- dependency>
- "1.0" encoding="UTF-8"?>
- <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
- updateCheck="false">
-
- <diskStore path="java.io.tmpdir"/>
-
- <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
- timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
-
-
-
-
- ehcache>
- <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
- <property name="configLocation" value="classpath:ehcache.xml"/>
- <property name="shared" value="true">property>
- bean>
- <bean id="shrioEhcache" class="org.apache.shiro.cache.ehcache.EhCacheManager">
- <property name="cacheManager" ref="cacheManagerFactory">property>
- bean>
-
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <property name="realm" ref="shiroRealm" />
-
- <property name="cacheManager" ref="shrioEhcache">property>
- bean>
session中保存的数据,只要session未结束,在任何地方都可以访问session值
必须要先登陆用户,使session里面有用户登陆的值,然后再访问session的值。
mapper不用编写,因为咱们目的只是测试session获取值
service层
- @Service
- public class SessionService implements ISession{
-
- @Override
- public void Sessiontest() {
- Session session = SecurityUtils.getSubject().getSession();
- System.out.println("sesion is"+session.getAttribute("user"));
- }
- }
首先,登陆的Controller保存session的值,然后通过编写SessionController获取session的值
- @RequestMapping("user/login")
- public String login(User user, Model model, HttpSession session) {
- Subject subject = SecurityUtils.getSubject();
-
- UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(),user.getPassword());
-
- try {
- subject.login(token);
- session.setAttribute("user",user);
- } catch (UnknownAccountException | LockedAccountException e) {
- model.addAttribute("message", e.getMessage());
- return "login";
- } catch (AuthenticationException e) {
- e.printStackTrace();
- model.addAttribute("message", "密码错误");
- return "login";
- }
-
- return "index";
- }
编写一个简单SessionController得到session的值
- @Controller
- public class SessionController {
-
- @Autowired
- private ISession iSession;
-
- @RequestMapping("Sessiontest")
- public Object Sessiontest(){
- iSession.Sessiontest();
- return "";
- }
- }
效果展示

效果展示

用于监听session的创建,过期等事件,如果在需要时可以再session创建时做些初始化操作,或在过期时做些清理操作。
1) 创建一个自定义监听器
- @Slf4j//做为日志输出
- public class SessionListener extends SessionListenerAdapter {
-
- @Override
- public void onStart(Session session) {
- log.info("Shiro session onStart .... ");
- }
-
- @Override
- public void onStop(Session session) {
- log.info("Shiro session onStop .... ");
- }
-
- @Override
- public void onExpiration(Session session) {
- log.info("Shiro session onExpiration ....");
- }
-
- }
2)配置文件,在spring配置文件中做如下配置
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <property name="realm" ref="shiroRealm" />
-
- <property name="cacheManager" ref="shrioEhcache"/>
-
- <property name="sessionManager" ref="sessionManager"/>
- bean>
-
-
- <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
- <property name="sessionListeners">
- <list>
- <bean class="com.zking.shirodemo.listener.SessionListener"/>
- list>
- property>
- bean>
登陆后
由此可见,我们日志只打印了一个sql语句,说明只在数据库执行一次
1)session持久化在applicationContext-base.xml配置文件
-
- <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
- <property name="sessionListeners">
- <list>
- <bean class="com.zking.spring.listener.SessionListener"/>
- list>
- property>
-
- <property name="sessionDAO" ref="sessionDao"/>
- bean>
-
-
-
- <bean id="sessionDao" class="com.zking.spring.listener.DbSessionDao">
- bean>
2)将session持久化加入安全管理器
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
-
- <property name="realm" ref="shiroRealm" />
-
- <property name="cacheManager" ref="shrioEhcache">property>
-
- <property name="sessionManager" ref="sessionManager"/>
- bean>
3)实现序列化接口,文章最后会附上序列化类
4)因为session要写CRUD操作,mapper层service层就不显示了
5)编写自定义的session,实现持久化必须要实现EnterpriseCacheSessionDAO接口,重写父类CRUD方法。
- /**
- * 自定义Session持久化,将Shiro的Session数据保存到数据库中。
- * 完成该类的编写后,需要在spring配置文件中进行配置
- */
- @Slf4j
- public class DbSessionDao extends EnterpriseCacheSessionDAO {
-
- @Autowired
- private ISessionModel sessionService;
-
- @Override
- protected Serializable doCreate(Session session) {
- Serializable sid = super.doCreate(session);
- SessionModel model = new SessionModel();
- model.setSessionId(sid.toString());
- model.setSession(SerializableUtil.serialize(session));
- log.debug("将session保存到数据库, sessionId = {}", sid);
- sessionService.addSession(model);
- return sid;
- }
-
-
- @Override
- protected Session doReadSession(Serializable sessionId) {
-
- Session session = super.doReadSession(sessionId);
-
- //如果从内存中获取了session,则直接返回
- if (!Objects.isNull(session)) {
- log.debug("从内存中获取session,sessionId = " + sessionId + ", 直接返回");
- return session;
- }
-
- log.debug("从内存中没有获取到session,id={}, 将从数据库获取session", sessionId);
- SessionModel model = new SessionModel();
- model.setSessionId(sessionId.toString());
- session = (Session) sessionService.getSession(model);
-
- if(Objects.isNull(session)) {
- log.debug("数据库中也没有找到id={}的session,将返回null");
- }
-
- return session;
- }
-
-
- //删除session时,需要将数据表中的记录一并删除
- @Override
- protected void doDelete(Session session) {
- SessionModel model = new SessionModel();
- model.setSessionId(session.getId().toString());
- log.debug("删除session,sessionId: " + session.getId().toString());
- sessionService.delSession(model);
- super.doDelete(session);
- }
-
-
- //更新session
- @Override
- protected void doUpdate(Session session) {
-
- String sessionId = session.getId().toString();
- SessionModel tmpModel = new SessionModel();
- tmpModel.setSessionId(sessionId);
-
- SessionModel model = sessionService.getSession(tmpModel);
-
- if(Objects.isNull(model)) {
-
- Object obj = session.getAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
-
- //数据库中是否有session,如果没有检查session无效,则直接返回,否则保存到数据库中
- if(Objects.isNull(obj) || !Boolean.parseBoolean(obj.toString())) {
- return ;
- }
-
- SessionModel saveModel = new SessionModel();
- saveModel.setSessionId(session.getId().toString());
- saveModel.setSession(SerializableUtil.serialize(session));
- log.debug("session已经过验证,且在数据库中不存在,将session保存到数据库 ..... ");
- sessionService.addSession(saveModel);
- } else {
- //如果session在数据库中已存在,则更新session
- model.setSession(SerializableUtil.serialize(session));
- log.debug("session在数据库中已存在,将session更新到数据库 ..... ");
- sessionService.updateSession(model);
- }
-
- //调用父类方法,更新session
- super.doUpdate(session);
- }
- }
附1:序列化实现类
- public final class SerializableUtil {
-
- private SerializableUtil() {}
-
-
- /**
- * Session序列化
- * @param session 待序列化的session
- * @return String
- */
- public static String serialize(Session session) {
-
- try {
- //ByteArrayOutputStream 用于存储序列化的Session对象
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
- //将Object对象输出成byte数据
- ObjectOutputStream out = new ObjectOutputStream(bos);
- out.writeObject(session);
-
- //将字节码,编码成String类型数据
- return Base64.getEncoder().encodeToString(bos.toByteArray());
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException("序列化失败");
- }
-
- }
-
-
- /**
- * session的反向序列化
- * @param sessionString 需要被反向序列化的对象
- * @return
- */
- public static Session deserialize(String sessionString) {
- try {
- //读取字节码表
- ByteArrayInputStream bis = new ByteArrayInputStream(Base64.getDecoder().decode(sessionString));
-
- //将字节码反序列化成 对象
- ObjectInputStream in = new ObjectInputStream(bis);
- Session session = (Session) in.readObject();
- return session;
- } catch (Exception e) {
- throw new RuntimeException("反序列化失败");
- }
- }
-
- }