用户工具

站点工具


分享:技术:shiro:spring整合shiro实现会话管理

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

后一修订版
前一修订版
分享:技术:shiro:spring整合shiro实现会话管理 [2015/09/20 23:02]
gxx 创建
分享:技术:shiro:spring整合shiro实现会话管理 [2015/09/22 16:01] (当前版本)
gxx [不同节点共享会话]
行 1: 行 1:
 ====== spring整合shiro实现会话管理 ====== ====== spring整合shiro实现会话管理 ======
 +本wiki是在[[分享:​技术:​shiro:​spring整合shiro实现权限管理|spring整合shiro实现权限管理]]的基础上继续修改实现的会话管理
 +===== application-shiro.xml =====
 +<code xml application-shiro.xml>​
 +<?xml version="​1.0"​ encoding="​UTF-8"?>​
 +<beans xmlns="​http://​www.springframework.org/​schema/​beans"​
 + xmlns:​xsi="​http://​www.w3.org/​2001/​XMLSchema-instance"​ xmlns:​aop="​http://​www.springframework.org/​schema/​aop"​
 + xmlns:​context="​http://​www.springframework.org/​schema/​context"​ xmlns:​tx="​http://​www.springframework.org/​schema/​tx"​
 + xmlns:​jpa="​http://​www.springframework.org/​schema/​data/​jpa"​
 + xmlns:​security="​http://​www.springframework.org/​schema/​security"​
 + xsi:​schemaLocation="​
 + http://​www.springframework.org/​schema/​beans ​
 + http://​www.springframework.org/​schema/​beans/​spring-beans-4.0.xsd
 + http://​www.springframework.org/​schema/​util
 + http://​www.springframework.org/​schema/​util/​spring-util-4.0.xsd
 + http://​www.springframework.org/​schema/​context
 + http://​www.springframework.org/​schema/​context/​spring-context-4.0.xsd">​
 +
 + <!-- 继承自AuthorizingRealm的自定义Realm,​即指定Shiro验证用户登录的类为自定义的ManageAuthorizingRealm.java -->
 + <bean id="​manageAuthorizingRealm"​ class="​com.gxx.manage.shiro.ManageAuthorizingRealm"​ />
 +
 + <!-- Shiro默认会使用Servlet容器的Session,​可通过sessionMode属性来指定使用Shiro原生Session -->
 + <!-- 即<​property name="​sessionMode"​ value="​native"/>,​详细说明见官方文档 -->
 + <!-- 这里主要是设置自定义的单Realm应用,​若有多个Realm,​可使用'​realms'​属性代替 -->
 + <bean id="​securityManager"​ class="​org.apache.shiro.web.mgt.DefaultWebSecurityManager">​
 + <​property name="​realm"​ ref="​manageAuthorizingRealm"​ />
 +        <​property name="​sessionManager"​ ref="​defaultWebSessionManager"/>​
 +        <​property name="​cacheManager"​ ref="​customShiroCacheManager"/>​
 + </​bean>​
 +
 +    <!-- default web session manager,session expire time 20 minutes -->
 +    <bean id="​defaultWebSessionManager"​ class="​org.apache.shiro.web.session.mgt.DefaultWebSessionManager">​
 +        <​property name="​globalSessionTimeout"​ value="​1200000"/>​
 +        <​property name="​sessionDAO"​ ref="​customShiroSessionDAO"/>​
 +        <​property name="​sessionIdCookie"​ ref="​simpleCookie"/>​
 +        <!-- <​property name="​sessionListeners">​
 +            <​list>​
 +                <ref local="​customSessionListener"/>​
 +            </​list>​
 +        </​property>​ -->
 +        <​property name="​sessionValidationScheduler"​ ref="​sessionValidationScheduler"/>​
 +        <​property name="​sessionValidationSchedulerEnabled"​ value="​true"/>​
 +        <​property name="​deleteInvalidSessions"​ value="​true"/>​
 +    </​bean>​
 +
 +    <bean id="​sessionValidationScheduler"​
 +          class="​org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">​
 +        <​property name="​sessionManager"​ ref="​defaultWebSessionManager"/>​
 +        <​property name="​interval"​ value="​1800000"/>​
 +    </​bean>​
 +
 +    <bean id="​simpleCookie"​ class="​org.apache.shiro.web.servlet.SimpleCookie">​
 +        <​constructor-arg index="​0"​ value="​JSESSIONID_COOKIE"/>​
 +        <​property name="​httpOnly"​ value="​true"/>​
 +    </​bean>​
 +    ​
 +    <!-- custom sessionDAO,​must use class impl CustomShiroSessionRepository interface -->
 +    <bean id="​customShiroSessionDAO"​ class="​com.gxx.manage.shiro.session.CustomShiroSessionDAO">​
 +        <​property name="​shiroSessionRepository"​ ref="​jedisShiroSessionRepository"/>​
 +    </​bean>​
 +
 +    <!-- custom save session by redis class impl ShiroSessionRepository interface -->
 +    <bean id="​jedisShiroSessionRepository"​ class="​com.gxx.manage.shiro.session.JedisShiroSessionRepository">​
 +        <​property name="​jedisManager"​ ref="​jedisManager"/>​
 +    </​bean>​
 +    ​
 +    <!-- redis manager class -->
 +    <bean id="​jedisManager"​ class="​com.gxx.manage.shiro.JedisManager">​
 +        <​property name="​jedisPool"​ ref="​jedisPool"/>​
 +    </​bean>​
 +    ​
 +    <!-- redis pool -->
 +    <bean id="​jedisPool"​ class="​redis.clients.jedis.JedisPool">​
 +        <​constructor-arg index="​0"​ ref="​poolConfig"/>​
 +        <​constructor-arg index="​1"​ value="​${redis.host}"/>​
 +        <​constructor-arg index="​2"​ value="​${redis.port}"/>​
 +        <​constructor-arg index="​3"​ value="​2000"/>​
 +        <​constructor-arg index="​4"​ value="​${redis.pass}"/>​
 +        <​constructor-arg index="​5"​ value="​0"/>​
 +    </​bean>​
 +    ​
 +    <!-- custom shiro authorization info cache manager class-->
 +    <bean id="​customShiroCacheManager"​ class="​com.gxx.manage.shiro.cache.CustomShiroCacheManager">​
 +        <​property name="​shiroCacheManager"​ ref="​jedisShiroCacheManager"/>​
 +    </​bean>​
 +    ​
 +    <!-- ShiroCacheManager interface impl class by redis,save authorization info to redis -->
 +    <bean id="​jedisShiroCacheManager"​ class="​com.gxx.manage.shiro.cache.JedisShiroCacheManager">​
 +        <​property name="​jedisManager"​ ref="​jedisManager"/>​
 +    </​bean>​
 +
 + <!-- Shiro主过滤器本身功能十分强大,​其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 -->
 + <!-- Web应用中,​Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,​Shiro对基于Spring的Web应用提供了完美的支持 -->
 + <bean id="​shiroFilter"​ class="​org.apache.shiro.spring.web.ShiroFilterFactoryBean">​
 + <!-- Shiro的核心安全接口,​这个属性是必须的 -->
 + <​property name="​securityManager"​ ref="​securityManager"​ />
 + <!-- 要求登录时的链接(可根据项目的URL进行替换),​非必须的属性,​默认会自动寻找Web工程根目录下的"/​login.jsp"​页面 -->
 + <​property name="​loginUrl"​ value="/​login.htm"​ />
 + <!-- 登录成功后要跳转的连接(本例中此属性用不到,​因为登录成功后的处理逻辑在LoginController里硬编码为main.jsp了) -->
 + <!-- <​property name="​successUrl"​ value="/​system/​main"/>​ -->
 + <!-- 用户访问未对其授权的资源时,​所显示的连接 -->
 + <!-- 若想更明显的测试此属性可以修改它的值,​如unauthor.jsp,​然后用[玄玉]登录后访问/​admin/​listUser.jsp就看见浏览器会显示unauthor.jsp -->
 + <​property name="​unauthorizedUrl"​ value="/​unauthorized.htm"​ />
 + <!-- 过滤器定义 -->
 + <​property name="​filters">  ​
 +         <​map>  ​
 +             <entry key="​perms"​ value-ref="​urlPermissionsFilter"​ />  ​
 +         </​map>  ​
 +     </​property>​
 + <!-- Shiro连接约束配置,​即过滤链的定义 -->
 + <!-- 此处可配合我的这篇文章来理解各个过滤连的作用http://​blog.csdn.net/​jadyer/​article/​details/​12172839 -->
 + <!-- 下面value值的第一个'/'​代表的路径是相对于HttpServletRequest.getContextPath()的值来的 -->
 + <!-- anon:它对应的过滤器里面是空的,​什么都没做,​这里.do和.jsp后面的*表示参数,​比方说login.jsp?​main这种 -->
 + <!-- authc:该过滤器下的页面必须验证后才能访问,​它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter -->
 + <​property name="​filterChainDefinitions">​
 + <​value>​
 + /​login.htm=anon
 + /​logout.htm=logout
 + /​unauthorized.htm=anon
 + /** = authc,perms
 + </​value>​
 + </​property>​
 + </​bean>​
 +
 + <!-- 自定义鉴权拦截器 -->
 + <!-- 基于URL的权限判断过滤器 我们自动根据URL产生所谓的权限字符串,这一项在Shiro示例中是写在配置文件里面的,默认认为权限不可动态配置 -->
 + <bean id="​urlPermissionsFilter"​ class="​com.gxx.manage.shiro.UrlPermissionsFilter"​ />
 +
 + <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
 + <bean id="​lifecycleBeanPostProcessor"​ class="​org.apache.shiro.spring.LifecycleBeanPostProcessor"​ />
 +
 + <!-- 开启Shiro的注解(如@RequiresRoles,​@RequiresPermissions),​需借助SpringAOP扫描使用Shiro注解的类,​并在必要时进行安全逻辑验证 -->
 + <!-- 配置以下两个bean即可实现此功能 -->
 + <!-- Enable Shiro Annotations for Spring-configured beans. Only run after 
 + the lifecycleBeanProcessor has run -->
 + <!-- 由于本例中并未使用Shiro注解,​故注释掉这两个bean(个人觉得将权限通过注解的方式硬编码在程序中,​查看起来不是很方便,​没必要使用) -->
 + <​!-- ​
 + <bean class="​org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" ​
 + depends-on="​lifecycleBeanPostProcessor"/>​
 + <bean class="​org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> ​
 + <​property name="​securityManager"​ ref="​securityManager"/>​
 + </​bean>​
 + -->
 +</​beans>​
 +</​code>​
 +===== SerializeUtil.java =====
 +<code java SerializeUtil.java>​
 +package com.gxx.manage.shiro;​
 +
 +import java.io.*;
 +
 +/**
 + * <dl>
 + ​* ​   <​dt><​b>​Title:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   序列化工具类
 + ​* ​   </dd>
 + ​* ​   <​dt><​b>​Description:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   <​p>​none
 + ​* ​   </dd>
 + * </dl>
 + *
 + * @author Administrator
 + * @version 1.0, 2015年9月22日
 + * @since manage
 + *
 + */
 +public class SerializeUtil {
 +
 +    public static byte[] serialize(Object value) {
 +        if (value == null) {
 +            throw new NullPointerException("​Can'​t serialize null"​);​
 +        }
 +        byte[] rv = null;
 +        ByteArrayOutputStream bos = null;
 +        ObjectOutputStream os = null;
 +        try {
 +            bos = new ByteArrayOutputStream();​
 +            os = new ObjectOutputStream(bos);​
 +            os.writeObject(value);​
 +            os.close();
 +            bos.close();​
 +            rv = bos.toByteArray();​
 +        } catch (Exception e) {
 +            e.printStackTrace();​
 +            System.out.println("​serialize error"​);​
 +        } finally {
 +            close(os);
 +            close(bos);
 +        }
 +        return rv;
 +    }
 +
 +    public static Object deserialize(byte[] in) {
 +        return deserialize(in,​ Object.class);​
 +    }
 +
 +    @SuppressWarnings("​unchecked"​)
 +    public static <T> T deserialize(byte[] in, Class<​T>​ requiredType) {
 +        Object rv = null;
 +        ByteArrayInputStream bis = null;
 +        ObjectInputStream is = null;
 +        try {
 +            if (in != null) {
 +                bis = new ByteArrayInputStream(in);​
 +                is = new ObjectInputStream(bis);​
 +                rv = is.readObject();​
 +            }
 +        } catch (Exception e) {
 +            e.printStackTrace();​
 +            System.out.println("​deserialize error"​);​
 +        } finally {
 +            close(is);
 +            close(bis);
 +        }
 +        return (T) rv;
 +    }
 +
 +    private static void close(Closeable closeable) {
 +        if (closeable != null)
 +            try {
 +                closeable.close();​
 +            } catch (IOException e) {
 +                e.printStackTrace();​
 +                System.out.println("​close stream error"​);​
 +            }
 +    }
 +
 +}
 +</​code>​
 +===== JedisManager.java =====
 +<code java JedisManager.java>​
 +package com.gxx.manage.shiro;​
 +
 +import redis.clients.jedis.Jedis;​
 +import redis.clients.jedis.JedisPool;​
 +import redis.clients.jedis.JedisPoolConfig;​
 +import redis.clients.jedis.exceptions.JedisConnectionException;​
 +
 +/**
 + * <dl>
 + ​* ​   <​dt><​b>​Title:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   Jedis工具类
 + ​* ​   </dd>
 + ​* ​   <​dt><​b>​Description:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   <​p>​none
 + ​* ​   </dd>
 + * </dl>
 + *
 + * @author Administrator
 + * @version 1.0, 2015年9月22日
 + * @since manage
 + *
 + */
 +public class JedisManager {
 +
 +    private JedisPool jedisPool;
 +
 +    public Jedis getJedis() {
 +        Jedis jedis = null;
 +        try {
 +            jedis = getJedisPool().getResource();​
 +        } catch (Exception e) {
 +            throw new JedisConnectionException(e);​
 +        }
 +        return jedis;
 +    }
 +
 +    public void returnResource(Jedis jedis, boolean isBroken) {
 +        if (jedis == null)
 +            return;
 +        if (isBroken)
 +            getJedisPool().returnBrokenResource(jedis);​
 +        else
 +            getJedisPool().returnResource(jedis);​
 +    }
 +
 +    public byte[] getValueByKey(int dbIndex, byte[] key) throws Exception {
 +        Jedis jedis = null;
 +        byte[] result = null;
 +        boolean isBroken = false;
 +        try {
 +            jedis = getJedis();
 +            jedis.select(dbIndex);​
 +            result = jedis.get(key);​
 +        } catch (Exception e) {
 +            isBroken = true;
 +            throw e;
 +        } finally {
 +            returnResource(jedis,​ isBroken);
 +        }
 +        return result;
 +    }
 +
 +    public void deleteByKey(int dbIndex, byte[] key) throws Exception {
 +        Jedis jedis = null;
 +        boolean isBroken = false;
 +        try {
 +            jedis = getJedis();
 +            jedis.select(dbIndex);​
 +            jedis.del(key);​
 +        } catch (Exception e) {
 +            isBroken = true;
 +            throw e;
 +        } finally {
 +            returnResource(jedis,​ isBroken);
 +        }
 +    }
 +
 +    public void saveValueByKey(int dbIndex, byte[] key, byte[] value, int expireTime)
 +            throws Exception {
 +        Jedis jedis = null;
 +        boolean isBroken = false;
 +        try {
 +            jedis = getJedis();
 +            jedis.select(dbIndex);​
 +            jedis.set(key,​ value);
 +            if (expireTime > 0)
 +                jedis.expire(key,​ expireTime);​
 +        } catch (Exception e) {
 +            isBroken = true;
 +            throw e;
 +        } finally {
 +            returnResource(jedis,​ isBroken);
 +        }
 +    }
 +
 +    public JedisPool getJedisPool() {
 +        return jedisPool;
 +    }
 +
 +    public void setJedisPool(JedisPool jedisPool) {
 +    /**
 +    * 配置文件注入有问题,这里手动创建
 +    * TODO
 +    */
 +    JedisPoolConfig config = new JedisPoolConfig();​
 +    config.setMaxTotal(1000); ​
 +    config.setMaxIdle(600); ​
 +    config.setMinIdle(300); ​
 +    config.setTestOnBorrow(true); ​
 +    JedisPool pool = new JedisPool(config,​ "​121.43.104.34",​ 6379, 20000);
 +        this.jedisPool = pool;
 +    }
 +}
 +</​code>​
 +===== CustomShiroSessionDAO.java =====
 +<code java CustomShiroSessionDAO.java>​
 +package com.gxx.manage.shiro.session;​
 +
 +import org.apache.shiro.session.Session;​
 +import org.apache.shiro.session.UnknownSessionException;​
 +import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;​
 +
 +import java.io.Serializable;​
 +import java.util.Collection;​
 +
 +/**
 + * <dl>
 + ​* ​   <​dt><​b>​Title:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   自定义shiro会话DAO
 + ​* ​   </dd>
 + ​* ​   <​dt><​b>​Description:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   <​p>​none
 + ​* ​   </dd>
 + * </dl>
 + *
 + * @author Administrator
 + * @version 1.0, 2015年9月22日
 + * @since manage
 + *
 + */
 +public class CustomShiroSessionDAO extends AbstractSessionDAO {
 +
 +    private ShiroSessionRepository shiroSessionRepository;​
 +
 +    @Override
 +    public void update(Session session) throws UnknownSessionException {
 +        getShiroSessionRepository().saveSession(session);​
 +    }
 +
 +    @Override
 +    public void delete(Session session) {
 +        if (session == null) {
 +            return;
 +        }
 +        Serializable id = session.getId();​
 +        if (id != null) {
 +            getShiroSessionRepository().deleteSession(id);​
 +        }
 +        //TODO if session is too large,when session destory clear shiro cache
 +    }
 +
 +    @Override
 +    public Collection<​Session>​ getActiveSessions() {
 +        return getShiroSessionRepository().getAllSessions();​
 +    }
 +
 +    @Override
 +    protected Serializable doCreate(Session session) {
 +        Serializable sessionId = this.generateSessionId(session);​
 +        this.assignSessionId(session,​ sessionId);
 +        getShiroSessionRepository().saveSession(session);​
 +        return sessionId;
 +    }
 +
 +    @Override
 +    protected Session doReadSession(Serializable sessionId) {
 +        return getShiroSessionRepository().getSession(sessionId);​
 +    }
 +
 +    public ShiroSessionRepository getShiroSessionRepository() {
 +        return shiroSessionRepository;​
 +    }
 +
 +    public void setShiroSessionRepository(
 +            ShiroSessionRepository shiroSessionRepository) {
 +        this.shiroSessionRepository = shiroSessionRepository;​
 +    }
 +
 +}
 +</​code>​
 +===== JedisShiroSessionRepository.java =====
 +<code java JedisShiroSessionRepository.java>​
 +package com.gxx.manage.shiro.session;​
 +
 +import java.io.Serializable;​
 +import java.util.Collection;​
 +
 +import org.apache.shiro.session.Session;​
 +
 +import com.gxx.manage.shiro.JedisManager;​
 +import com.gxx.manage.shiro.SerializeUtil;​
 +
 +/**
 + * <dl>
 + ​* ​   <​dt><​b>​Title:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   Jedis实现shiro会话管理接口
 + ​* ​   </dd>
 + ​* ​   <​dt><​b>​Description:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   <​p>​none
 + ​* ​   </dd>
 + * </dl>
 + *
 + * @author Administrator
 + * @version 1.0, 2015年9月22日
 + * @since manage
 + *
 + */
 +public class JedisShiroSessionRepository implements ShiroSessionRepository {
 +
 +    private static final String REDIS_SHIRO_SESSION = "​shiro-session:";​
 +    private static final int SESSION_VAL_TIME_SPAN = 18000;
 +    private static final int DB_INDEX = 0;
 +
 +    private JedisManager jedisManager;​
 +
 +    @Override
 +    public void saveSession(Session session) {
 +        if (session == null || session.getId() == null)
 +            throw new NullPointerException("​session is empty"​);​
 +        try {
 +            byte[] key = SerializeUtil.serialize(buildRedisSessionKey(session.getId()));​
 +            byte[] value = SerializeUtil.serialize(session);​
 +            long sessionTimeOut = session.getTimeout() / 1000;
 +            Long expireTime = sessionTimeOut + SESSION_VAL_TIME_SPAN + (5 * 60);
 +            getJedisManager().saveValueByKey(DB_INDEX,​ key, value, expireTime.intValue());​
 +        } catch (Exception e) {
 +            e.printStackTrace();​
 +        }
 +    }
 +
 +    @Override
 +    public void deleteSession(Serializable id) {
 +        if (id == null) {
 +            throw new NullPointerException("​session id is empty"​);​
 +        }
 +        try {
 +            getJedisManager().deleteByKey(DB_INDEX,​
 +                    SerializeUtil.serialize(buildRedisSessionKey(id)));​
 +        } catch (Exception e) {
 +            e.printStackTrace();​
 +        }
 +    }
 +
 +    @Override
 +    public Session getSession(Serializable id) {
 +        if (id == null)
 +            throw new NullPointerException("​session id is empty"​);​
 +        Session session = null;
 +        try {
 +            byte[] value = getJedisManager().getValueByKey(DB_INDEX,​ SerializeUtil
 +                    .serialize(buildRedisSessionKey(id)));​
 +            session = SerializeUtil.deserialize(value,​ Session.class);​
 +        } catch (Exception e) {
 +            e.printStackTrace();​
 +        }
 +        return session;
 +    }
 +
 +    @Override
 +    public Collection<​Session>​ getAllSessions() {
 +        //TODO
 +        return null;
 +    }
 +
 +    private String buildRedisSessionKey(Serializable sessionId) {
 +        return REDIS_SHIRO_SESSION + sessionId;
 +    }
 +
 +    public JedisManager getJedisManager() {
 +        return jedisManager;​
 +    }
 +
 +    public void setJedisManager(JedisManager jedisManager) {
 +        this.jedisManager = jedisManager;​
 +    }
 +}
 +</​code>​
 +===== ShiroSessionRepository.java =====
 +<code java ShiroSessionRepository.java>​
 +package com.gxx.manage.shiro.session;​
 +
 +import org.apache.shiro.session.Session;​
 +
 +import java.io.Serializable;​
 +import java.util.Collection;​
 +
 +/**
 + * <dl>
 + ​* ​   <​dt><​b>​Title:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   自定义shiro会话管理接口
 + ​* ​   </dd>
 + ​* ​   <​dt><​b>​Description:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   <​p>​none
 + ​* ​   </dd>
 + * </dl>
 + *
 + * @author Administrator
 + * @version 1.0, 2015年9月22日
 + * @since manage
 + *
 + */
 +public interface ShiroSessionRepository {
 +
 +    void saveSession(Session session);
 +
 +    void deleteSession(Serializable sessionId);
 +
 +    Session getSession(Serializable sessionId);
 +
 +    Collection<​Session>​ getAllSessions();​
 +}
 +</​code>​
 +===== CustomShiroCacheManager.java =====
 +<code java CustomShiroCacheManager.java>​
 +package com.gxx.manage.shiro.cache;​
 +
 +import org.apache.shiro.cache.Cache;​
 +import org.apache.shiro.cache.CacheException;​
 +import org.apache.shiro.cache.CacheManager;​
 +import org.apache.shiro.util.Destroyable;​
 +
 +/**
 + * <dl>
 + ​* ​   <​dt><​b>​Title:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   shiro缓存管理类
 + ​* ​   </dd>
 + ​* ​   <​dt><​b>​Description:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   <​p>​none
 + ​* ​   </dd>
 + * </dl>
 + *
 + * @author Administrator
 + * @version 1.0, 2015年9月22日
 + * @since manage
 + *
 + */
 +public class CustomShiroCacheManager implements CacheManager,​ Destroyable {
 +
 +    private ShiroCacheManager shiroCacheManager;​
 +
 +    @Override
 +    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
 +        return getShiroCacheManager().getCache(name);​
 +    }
 +
 +    @Override
 +    public void destroy() throws Exception {
 +        shiroCacheManager.destroy();​
 +    }
 +
 +    public ShiroCacheManager getShiroCacheManager() {
 +        return shiroCacheManager;​
 +    }
 +
 +    public void setShiroCacheManager(ShiroCacheManager shiroCacheManager) {
 +        this.shiroCacheManager = shiroCacheManager;​
 +    }
 +
 +}
 +</​code>​
 +===== JedisShiroCache.java =====
 +<code java JedisShiroCache.java>​
 +package com.gxx.manage.shiro.cache;​
 +
 +import java.util.Collection;​
 +import java.util.Set;​
 +
 +import org.apache.shiro.cache.Cache;​
 +import org.apache.shiro.cache.CacheException;​
 +
 +import com.gxx.manage.shiro.JedisManager;​
 +import com.gxx.manage.shiro.SerializeUtil;​
 +
 +/**
 + * <dl>
 + ​* ​   <​dt><​b>​Title:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   Jedis实现Shiro缓存
 + ​* ​   </dd>
 + ​* ​   <​dt><​b>​Description:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   <​p>​none
 + ​* ​   </dd>
 + * </dl>
 + *
 + * @author Administrator
 + * @version 1.0, 2015年9月22日
 + * @since manage
 + *
 + */
 +public class JedisShiroCache<​K,​ V> implements Cache<K, V> {
 +
 +    private static final String REDIS_SHIRO_CACHE = "​shiro-cache:";​
 +    private static final int DB_INDEX = 1;
 +
 +    private JedisManager jedisManager;​
 +
 +    private String name;
 +
 +    public JedisShiroCache(String name, JedisManager jedisManager) {
 +        this.name = name;
 +        this.jedisManager = jedisManager;​
 +    }
 +
 +    /**
 +     * 自定义relm中的授权/​认证的类名加上授权/​认证英文名字
 +     */
 +    public String getName() {
 +        if (name == null)
 +            return "";​
 +        return name;
 +    }
 +
 +    public void setName(String name) {
 +        this.name = name;
 +    }
 +
 +    @SuppressWarnings("​unchecked"​)
 +    @Override
 +    public V get(K key) throws CacheException {
 +        byte[] byteKey = SerializeUtil.serialize(buildCacheKey(key));​
 +        byte[] byteValue = new byte[0];
 +        try {
 +            byteValue = jedisManager.getValueByKey(DB_INDEX,​ byteKey);
 +        } catch (Exception e) {
 +            e.printStackTrace();​
 +            System.out.println("​get cache error"​);​
 +        }
 +        return (V) SerializeUtil.deserialize(byteValue);​
 +    }
 +
 +    @Override
 +    public V put(K key, V value) throws CacheException {
 +        V previos = get(key);
 +        try {
 +            jedisManager.saveValueByKey(DB_INDEX,​ SerializeUtil.serialize(buildCacheKey(key)),​
 +                    SerializeUtil.serialize(value),​ -1);
 +        } catch (Exception e) {
 +            e.printStackTrace();​
 +            System.out.println("​put cache error"​);​
 +        }
 +        return previos;
 +    }
 +
 +    @Override
 +    public V remove(K key) throws CacheException {
 +        V previos = get(key);
 +        try {
 +            jedisManager.deleteByKey(DB_INDEX,​ SerializeUtil.serialize(buildCacheKey(key)));​
 +        } catch (Exception e) {
 +            e.printStackTrace();​
 +            System.out.println("​remove cache error"​);​
 +        }
 +        return previos;
 +    }
 +
 +    @Override
 +    public void clear() throws CacheException {
 +        //TODO
 +    }
 +
 +    @Override
 +    public int size() {
 +        if (keys() == null)
 +            return 0;
 +        return keys().size();​
 +    }
 +
 +    @Override
 +    public Set<​K>​ keys() {
 +        //TODO
 +        return null;
 +    }
 +
 +    @Override
 +    public Collection<​V>​ values() {
 +        //TODO
 +        return null;
 +    }
 +
 +    private String buildCacheKey(Object key) {
 +        return REDIS_SHIRO_CACHE + getName() + ":"​ + key;
 +    }
 +
 +}
 +</​code>​
 +===== JedisShiroCacheManager.java =====
 +<code java JedisShiroCacheManager.java>​
 +package com.gxx.manage.shiro.cache;​
 +
 +import org.apache.shiro.cache.Cache;​
 +
 +import com.gxx.manage.shiro.JedisManager;​
 +
 +/**
 + * <dl>
 + ​* ​   <​dt><​b>​Title:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   Jedis实现shiro缓存管理接口
 + ​* ​   </dd>
 + ​* ​   <​dt><​b>​Description:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   <​p>​none
 + ​* ​   </dd>
 + * </dl>
 + *
 + * @author Administrator
 + * @version 1.0, 2015年9月22日
 + * @since manage
 + *
 + */
 +public class JedisShiroCacheManager implements ShiroCacheManager {
 +
 +    private JedisManager jedisManager;​
 +
 +    @Override
 +    public <K, V> Cache<K, V> getCache(String name) {
 +        return new JedisShiroCache<​K,​ V>(name, getJedisManager());​
 +    }
 +
 +    @Override
 +    public void destroy() {
 +        getJedisManager().getJedis().shutdown();​
 +    }
 +
 +    public JedisManager getJedisManager() {
 +        return jedisManager;​
 +    }
 +
 +    public void setJedisManager(JedisManager jedisManager) {
 +        this.jedisManager = jedisManager;​
 +    }
 +}
 +</​code>​
 +===== ShiroCacheManager.java =====
 +<code java ShiroCacheManager.java>​
 +package com.gxx.manage.shiro.cache;​
 +
 +import org.apache.shiro.cache.Cache;​
 +
 +/**
 + * <dl>
 + ​* ​   <​dt><​b>​Title:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   shiro缓存管理接口
 + ​* ​   </dd>
 + ​* ​   <​dt><​b>​Description:</​b></​dt>​
 + ​* ​   <dd>
 + ​* ​   <​p>​none
 + ​* ​   </dd>
 + * </dl>
 + *
 + * @author Administrator
 + * @version 1.0, 2015年9月22日
 + * @since manage
 + *
 + */
 +public interface ShiroCacheManager {
 +
 +    <K, V> Cache<K, V> getCache(String name);
 +
 +    void destroy();
 +
 +}
 +</​code>​
 +===== SessionController.java =====
 +<code java SessionController.java>​
 +package com.gxx.manage.web.session;​
 +
 +import java.util.HashMap;​
 +import java.util.Map;​
 +
 +import javax.servlet.http.HttpServletRequest;​
 +
 +import org.apache.log4j.Logger;​
 +import org.apache.shiro.SecurityUtils;​
 +import org.apache.shiro.session.Session;​
 +import org.apache.shiro.subject.Subject;​
 +import org.springframework.stereotype.Controller;​
 +import org.springframework.web.bind.annotation.RequestMapping;​
 +import org.springframework.web.bind.annotation.ResponseBody;​
 +
 +/**
 + * UserController
 + ​* ​
 + * @author gxx
 + */
 +@Controller
 +@RequestMapping("/​session/"​)
 +public class SessionController {
 + /**
 + * 日志处理器
 + */
 + private final Logger logger = Logger.getLogger(SessionController.class);​
 +
 + /**
 + * 设置session
 + * @param request
 + * @return
 + */
 + @RequestMapping(value = "/​set",​produces="​application/​json"​)
 + public @ResponseBody Map<​String,​ Object> set(HttpServletRequest request) {
 + logger.info("​设置session:key=["​ + request.getParameter("​key"​) + "​],value=["​ + request.getParameter("​value"​) + "​]"​);​
 + /**
 + * 设置session
 + */
 + Subject currentUser = SecurityUtils.getSubject();​
 + Session session = currentUser.getSession();​
 + session.setAttribute(request.getParameter("​key"​),​ request.getParameter("​value"​));​
 + /**
 + * 返回结果
 + */
 + Map<​String,​ Object> resultMap = new HashMap<​String,​ Object>​();​
 + resultMap.put("​success",​ true);
 + return resultMap;
 + }
 +
 + /**
 + * 获取session
 + * @param request
 + * @return
 + */
 + @RequestMapping(value = "/​get",​produces="​application/​json"​)
 + public @ResponseBody Map<​String,​ Object> get(HttpServletRequest request) {
 + logger.info("​获取session:key=["​ + request.getParameter("​key"​) + "​]"​);​
 + /**
 + * 设置session
 + */
 + Subject currentUser = SecurityUtils.getSubject();​
 + Session session = currentUser.getSession();​
 + Object value = session.getAttribute(request.getParameter("​key"​));​
 + logger.info("​获取session:value=["​ + value + "​]"​);​
 + /**
 + * 返回结果
 + */
 + Map<​String,​ Object> resultMap = new HashMap<​String,​ Object>​();​
 + resultMap.put("​success",​ true);
 + resultMap.put("​value",​ value);
 + return resultMap;
 + }
 +}
 +</​code>​
 +===== 数据库数据截图 =====
 +{{:​分享:​技术:​shiro:​permission_1.png?​300|}}
 +{{:​分享:​技术:​shiro:​role_permission_1.png?​300|}}
 +===== 代码操作 =====
 +原来操作session是:
 +  request.getSession().setAttribute("​K","​V"​);​
 +  request.getSession().getAttribute("​K"​);​
 +shiro操作session是:
 +  Subject currentUser = SecurityUtils.getSubject();​
 +  Session session = currentUser.getSession();​
 +  session.setAttribute("​K","​V"​);​
 +  session.getAttribute("​K"​);​
 +===== 无状态节点重启依然保存会话 =====
 +有了shiro之后,会话的存取就不依赖容器了,各个应用节点就是无状态的,所有的会话存储都在redis中,即使容器挂掉,重启之后,用户从redis中读取会话信息依然可以保持登录状态以及获取之前设置的session数据,只要redis不挂(redis可以本地持久化,如果有必要redis还可以做集群)。
 +===== 不同节点共享会话 =====
 +同一机器下,不同端口P1和P2两个tomcat T1和T2,都部署相同的应用manage,指向同一个redis。T1访问登录成功,T2可以直接访问登录后页面;T1设置session的键Key和值Value,T2可以直接通过键Key取到对应的值Value。
分享/技术/shiro/spring整合shiro实现会话管理.1442761369.txt.gz · 最后更改: 2015/09/20 23:02 由 gxx