shiro开发文档阅读(三)sessionManager

Apache Shiro 提供安全框架界独一无二的东西:一个完整的企业级 Session 解决方案,从最简单的命令行及智能手机 应用到最大的集群企业 Web 应用程序。

这对许多应用有着很大的影响——直到 Shiro 出现,如果你需要 session 支持,你需要部署你的应用程序到 Web 容 器或使用 EJB 有状态会话 Bean。Shiro 的 Session 支持比这两种机制的使用和管理更为简单,而且它在适用于任何程 序,不论容器。 即使你在一个 Servlet 或 EJB 容器中部署你的应用程序,仍然有令人信服的理由来使用 Shiro 的 Session 支持而不是容 器的。

下面是一个 Shiro 的 Session 支持的最可取的功能列表:

1 POJO/J2SE based(IoC friendly) - Shiro 的一切(包括所有 Session 和 Session Management 方面)都是基于接口和 POJO 实现。这可以让你轻松地配置所有拥有任何 JavaBeans 兼容配置格式(如 JSON,YAML,Spring XML 或类 似的机制)的会话组件。你也可以轻松地扩展 Shiro 的组件或编写你自己所需的来完全自定义 session management。
2 . Easy Custom Session Storage - 因为 Shiro 的 Session 对象是基于 POJO 的,会话数据可以很容易地存储在任意 数量的数据源。这允许你自定义你的应用程序会话数据的确切位置——例如,文件系统,联网的分布式缓存, 关系数据库,或专有的数据存储。
3 Container-Independent Clustering! - Shiro 的会话可以很容易地聚集通过使用任何随手可用的网络缓存产品,像 Ehcache + Terracotta,Coherence,GigaSpaces,等等。这意味着你可以为 Shiro 配置会话群集一次且仅一次, 无论你部署到什么容器中,你的会话将以相同的方式聚集。不需要容器的具体配置!
4 Heterogeneous Client Access - 与 EJB 或 web 会话不同,Shiro 会话可以被各种客户端技术“共享”。例如,一 个桌面应用程序可以“看到”和“共享”同一个被使用的物理会话通过在 Web 应用程序中的同一用户。我们 不知道除了 Shiro 以外的其他框架能够支持这一点。
5 Event Listeners - 事件监听器允许你在会话生命周期监听生命周期事件。你可以侦听这些事件和对自定义应用 程序的行为作出反应——例如,更新用户记录当他们的会话过期时。
6 Host Address Retention - Shiro Sessions 从会话发起地方保留 IP 地址或主机名。这允许你确定用户所在,并作 出相应的反应(通常是在 IP 分配确定的企业内部网络环境)。
7 Inactivity/Expiration Support - 由于不活动导致会话过期如预期的那样,但它们可以延续很久通过 touch()方法 来保持它们“活着”,如果你希望的话。这在 RIA(富互联网应用)环境非常有用,用户可能会使用桌面应用程 序,但可能不会经常与服务器进行通信,但该服务器的会话不应过期。
8 Transparent Web Use - Shiro 的网络支持,充分地实现和支持关于 Sessions(HttpSession 接口和它的所有相关 的 API)的 Servlet2.5 规范.这意味着你可以使用在现有 Web 应用程序中使用 Shiro 会话,并且你不需要改变任 何现有的 Web 代码。
9 Can be used for SSO - 由于 Shiro 会话是基于 POJO 的,它们可以很容易地存储在任何数据源,而且它们可以跨 程序“共享”如果需要的话。我们称之为"poor man’s SSO",并它可以用来提供简单的登录体验,由于共享的 会话能够保留身份验证状态。

使用session

    Subject currentUser = SecurityUtils.getSubject();
    Session session = currentUser.getSession(); 
    session.setAttribute("someKey", someValue);   
 
    //subject.getSession()方法是调用 currentUser.getSubject(true)的快捷方式。   
    //对于那些熟悉 HttpServletRequest API 的,Subject.getSession(boolean create)方法与 HttpServletRequest.getSession(boolean create)方法有着异曲同工之效。 
    // 如果该 Subject 已经拥有一个 Session,则 boolean 参数被忽略且 Session 被立即返回。
    // 如果该 Subject 还没有一个 Session 且 create 参数为 true,则创建一个新的会话并返回该会话。
    //如果该 Subject 还没有一个 Session 且 create 参数为 false,则不会创建新的会话且返回 null。
    //当开发框架代码来确保一个 Session 没有被创建是没有必要的时候,subject.getSession(false)可以起到很好的作用

配置sessionManager

默认的 SecurityManger 实现是默认使用立即可用的 DefaultSessionManager
DefaultSessionManager 的实现提供一个 应用程序所需的所有企业级会话管理,如 Session 验证,orphan cleanup,等等。这可以在任何应用程序中使用。

   	   /**
         * session管理器
         * @return
         */
        @Bean
        public DefaultWebSessionManager sessionManager(){
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            // 设置session超时
            sessionManager.setGlobalSessionTimeout(1000*100);
            //定期验证session
            sessionManager.setSessionValidationSchedulerEnabled(true);
            // 删除无效session
            sessionManager.setDeleteInvalidSessions(true);
            // 设置JSESSIONID
            sessionManager.setSessionIdCookie(cookie());
            // 设置sessionDAO
            sessionManager.setSessionDAO(sessionDAO());
            sessionManager.setSessionIdCookieEnabled(true);
            return sessionManager;
        }

sessionDAO
什么是sessionDAO,有什么意义?

每当一个会话被创建或更新时,它的数据需要持久化到一个存储位置以便它能够被稍后的应用程序访问。同样地, 当一个会话失效且不再被使用时,它需要从存储中删除以便会话数据存储空间不会被耗尽。SessionManager 实现委 托这些 Create/Read/Update/Delete(CRUD)操作为内部组件,同时,SessionDAO,反映了数据访问对象(DAO)设计 模式。 SessionDAO 的权力是你能够实现该接口来与你想要的任何数据存储进行通信。这意味着你的会话数据可以驻留在内 存中,文件系统,关系数据库或 NoSQL 的数据存储,或其他任何你需要的位置。你得控制持久性行为。

shiro中的sessionDAO

    //顶层接口,定义了增删改查的方法
  
    public interface SessionDAO {
        Serializable create(Session var1);
    
        Session readSession(Serializable var1) throws UnknownSessionException;
    
        void update(Session var1) throws UnknownSessionException;
    
        void delete(Session var1);
    
        Collection<Session> getActiveSessions();
    }

    //抽象实现类
    public abstract class AbstractSessionDAO implements SessionDAO{
          ..........
    }

    //四个实现类
    public class RedisSessionDAO extends AbstractSessionDAO {}
    public abstract class CachingSessionDAO extends AbstractSessionDAO implements CacheManagerAware {}
    public class EnterpriseCacheSessionDAO extends CachingSessionDAO {}
    public class MemorySessionDAO extends AbstractSessionDAO {

重点关注下redisSessionDAO

    public class RedisSessionDAO extends AbstractSessionDAO {
        private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
        //需要设置下redis的缓存管理器
        private RedisManager redisManager;
        //缓存key前缀
        private String keyPrefix = "shiro_redis_session:";
    
        public RedisSessionDAO() {
        }
    
        //缓存更新
        public void update(Session session) throws UnknownSessionException {
            this.saveSession(session);
        }
    
    	//缓存session
        private void saveSession(Session session) throws UnknownSessionException {
            if (session != null && session.getId() != null) {
                byte[] key = this.getByteKey(session.getId());
                byte[] value = SerializeUtils.serialize(session);
                session.setTimeout((long)(this.redisManager.getExpire() * 1000));
                this.redisManager.set(key, value, this.redisManager.getExpire());
            } else {
                logger.error("session or session id is null");
            }
        }
    
        //删除缓存
        public void delete(Session session) {
            if (session != null && session.getId() != null) {
                this.redisManager.del(this.getByteKey(session.getId()));
            } else {
                logger.error("session or session id is null");
            }
        }
    
       //读取缓存
        public Collection<Session> getActiveSessions() {
            Set<Session> sessions = new HashSet();
            Set<byte[]> keys = this.redisManager.keys(this.keyPrefix + "*");
            if (keys != null && keys.size() > 0) {
                Iterator i$ = keys.iterator();
    
                while(i$.hasNext()) {
                    byte[] key = (byte[])i$.next();
                    Session s = (Session)SerializeUtils.deserialize(this.redisManager.get(key));
                    sessions.add(s);
                }
            }
    
            return sessions;
        }
    
        //序列化方法
        protected Serializable doCreate(Session session) {
            Serializable sessionId = this.generateSessionId(session);
            this.assignSessionId(session, sessionId);
            this.saveSession(session);
            return sessionId;
        }
    
       //读取session
        protected Session doReadSession(Serializable sessionId) {
            if (sessionId == null) {
                logger.error("session id is null");
                return null;
            } else {
                Session s = (Session)SerializeUtils.deserialize(this.redisManager.get(this.getByteKey(sessionId)));
                return s;
            }
        }
      
        //获取序列化后的字节类型的key
        private byte[] getByteKey(Serializable sessionId) {
            String preKey = this.keyPrefix + sessionId;
            return preKey.getBytes();
        }
    
        public RedisManager getRedisManager() {
            return this.redisManager;
        }
    
        public void setRedisManager(RedisManager redisManager) {
            this.redisManager = redisManager;
            this.redisManager.init();
        }
    
        public String getKeyPrefix() {
            return this.keyPrefix;
        }
    
        public void setKeyPrefix(String keyPrefix) {
            this.keyPrefix = keyPrefix;
        }
    }

设置redis的缓存管理器,shiro默认使用的是jedis实现

    public class RedisManager {
        private String host = "127.0.0.1";
        private int port = 6379;
        private int expire = 0;
        private int timeout = 0;
        private String password = "";
        private static JedisPool jedisPool = null;
    
        public RedisManager() {
        }
    
        public void init() {
            if (jedisPool == null) {
                if (this.password != null && !"".equals(this.password)) {
                    jedisPool = new JedisPool(new JedisPoolConfig(), this.host, this.port, this.timeout, this.password);
                } else if (this.timeout != 0) {
                    jedisPool = new JedisPool(new JedisPoolConfig(), this.host, this.port, this.timeout);
                } else {
                    jedisPool = new JedisPool(new JedisPoolConfig(), this.host, this.port);
                }
            }
    
        }
    
        public byte[] get(byte[] key) {
            byte[] value = null;
            Jedis jedis = (Jedis)jedisPool.getResource();
    
            byte[] value;
            try {
                value = jedis.get(key);
            } finally {
                jedisPool.returnResource(jedis);
            }
    
            return value;
        }
    
        public byte[] set(byte[] key, byte[] value) {
            Jedis jedis = (Jedis)jedisPool.getResource();
    
            try {
                jedis.set(key, value);
                if (this.expire != 0) {
                    jedis.expire(key, this.expire);
                }
            } finally {
                jedisPool.returnResource(jedis);
            }
    
            return value;
        }
    
        public byte[] set(byte[] key, byte[] value, int expire) {
            Jedis jedis = (Jedis)jedisPool.getResource();
    
            try {
                jedis.set(key, value);
                if (expire != 0) {
                    jedis.expire(key, expire);
                }
            } finally {
                jedisPool.returnResource(jedis);
            }
    
            return value;
        }
    
        public void del(byte[] key) {
            Jedis jedis = (Jedis)jedisPool.getResource();
    
            try {
                jedis.del(key);
            } finally {
                jedisPool.returnResource(jedis);
            }
    
        }
    
        public void flushDB() {
            Jedis jedis = (Jedis)jedisPool.getResource();
    
            try {
                jedis.flushDB();
            } finally {
                jedisPool.returnResource(jedis);
            }
    
        }
    
        public Long dbSize() {
            Long dbSize = 0L;
            Jedis jedis = (Jedis)jedisPool.getResource();
    
            try {
                dbSize = jedis.dbSize();
            } finally {
                jedisPool.returnResource(jedis);
            }
    
            return dbSize;
        }
    
        public Set<byte[]> keys(String pattern) {
            Set<byte[]> keys = null;
            Jedis jedis = (Jedis)jedisPool.getResource();
    
            try {
                keys = jedis.keys(pattern.getBytes());
            } finally {
                jedisPool.returnResource(jedis);
            }
    
            return keys;
        }
    
        public String getHost() {
            return this.host;
        }
    
        public void setHost(String host) {
            this.host = host;
        }
    
        public int getPort() {
            return this.port;
        }
    
        public void setPort(int port) {
            this.port = port;
        }
    
        public int getExpire() {
            return this.expire;
        }
    
        public void setExpire(int expire) {
            this.expire = expire;
        }
    
        public int getTimeout() {
            return this.timeout;
        }
    
        public void setTimeout(int timeout) {
            this.timeout = timeout;
        }
    
        public String getPassword() {
            return this.password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }

相信搞懂了这两个,sessionDAO也就顺理配置好了

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值