Apache Shiro安全框架(5)-会话管理和缓存

会话

会话,即用户保持和服务端之间的联系,保证用户在下一次访问服务端时不必在提交用户身份信息。而服务端可以通过用户提交的sessionId判断用户身份。

Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession()

可以通过以上方法获取当前登录用

/**
 *Subject.getSession(true),即如果当前没有创建session对象会创建一个;
 *Subject.getSession(false),如果当前没有创建session对象则返回null。
*/
Subject.getSession();//等价于Subject.getSession(true)

session.getId();//获取当前会话的唯一标识。

session.getHost();//获取当前会话的主机地址。

session.getTimeout() & session.setTimeout(毫秒);//设置/获取当前Session的过期时间。

/**获取会话的启动时间及最后访问时间;
 *如果是J2SE环境需要自己定期调用session.touch()去更新最后访问时间;
 *如果是Web环境,每次进入ShiroFilter都会自动调用session.touch()来更新最后访问时间。
*/
session.getStartTimestamp() & session.getLastAccessTime();

/**
 *更新会话最后访问时间以及销毁会话;
 *Subject.logout()会自动调用session.stop()。
 *在Web应用中,调用HttpSession.invalidate()也会自动调用session.stop()来销毁shiro的会话。
*/
session.touch() & session.stop();

session.setAttribute(key,val) & session.getAttribute(key) & session.removeAttribute(key);//设置/获取/删除 会话属性。


会话管理

shiro中的会话管理器管理所有Subject的会话的创建、删除、验证、失效等。shiro中会话管理的结构图如下:

Apache Shiro安全框架(5)-会话管理和缓存_第1张图片

发现SecurityManager继承了SessionManager接口,而SecurityManager结构图如下:

Apache Shiro安全框架(5)-会话管理和缓存_第2张图片

发现SessionSecurityManager实现了接口SecurityManager,实质SessionSecurityManager实现了把会话管理委托给对应的SessionManager。而DefaultSecurityManager和DefaultWebSecurityManager都继承了SessionSecurityManager

会话监听器

自定义类实现SessionListener接口,并实现其中的方法。SessionListener方法如下:

  • onStart(Session):创建会话时触发该事件
  • onStop(Session):销毁会话时触发该事件
  • onExpiration(Session):会话过期时触发该事件

Session的CURD

shiro中SessionDao的结构图

Apache Shiro安全框架(5)-会话管理和缓存_第3张图片

  • AbstractSessionDAO 提供了 SessionDAO 的基础实现,如生成会话 ID 等;
  • CachingSessionDAO 提供了对开发者透明的会话缓存的功能,只需要在SecurityManager设置相应的 CacheManager即可
  • MemorySessionDAO 直接在内存中进行会话维护;
  • EnterpriseCacheSessionDAO提供了缓存功能的会话维护,默认情况下使用 MapCache 实现,内部使用ConcurrentHashMap 保存缓存的会话

以上是shiro中会话管理的基本介绍,接下来看看在shiro中如何自定义会话管理:

自定义sessionDao:

import com.heyu.framework.service.RedisService;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;

public class RedisSessionDao extends EnterpriseCacheSessionDAO {

    @Autowired
    private RedisService  redisService;

    private static final String PREFIX = "shiro_session";

    private static Long expire = 3600L;

    @Override
    protected void doUpdate(Session session) {
        super.doUpdate(session);
        String key = PREFIX + session.getId().toString();
        if(!redisService.exists(key)){
            redisService.set(key,session);
        }
        redisService.expire(key,expire,TimeUnit.SECONDS);

    }

    @Override
    protected void doDelete(Session session) {
        super.doDelete(session);
        redisService.delete(PREFIX + session.getId());
    }

    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = super.doCreate(session);
        redisService.set(PREFIX + sessionId.toString(),session);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable serializable) {

        Session session = super.doReadSession(serializable);
        if(session == null){
            session = (Session) redisService.get(PREFIX + serializable.toString());
        }
        return session;
    }

}

通过该类中的方法名便知道,什么时候触发该方法的调用

配置会话管理:

   @Bean
    public RedisSessionDao redisSessionDao(){
        RedisSessionDao redisSessionDao = new RedisSessionDao();
        return redisSessionDao;
    }

    @Bean
    public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDao());
        return sessionManager;
    }

再将实例化SecurityManager中的SessionManager属性即可:

 @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //内部方法调用realm的getAuthenticationInfo获取认证信息
        securityManager.setRealm(authRealm());
        securityManager.setCacheManager(redisCacheManager());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

缓存管理

shiro中提供Cache的接口:

public interface Cache {
    //根据Key获取缓存中的值
    public V get(K key) throws CacheException;
    //往缓存中放入key-value,返回缓存中之前的值
    public V put(K key, V value) throws CacheException; 
    //移除缓存中key对应的值,返回该值
    public V remove(K key) throws CacheException;
    //清空整个缓存
    public void clear() throws CacheException;
    //返回缓存大小
    public int size();
    //获取缓存中所有的key
    public Set keys();
    //获取缓存中所有的value
    public Collection values();
}

提供的 CacheManager接口:

public interface CacheManager {
    //根据缓存名字获取一个Cache
    public  Cache getCache(String name) throws CacheException;
}

在shiro的内部组件SecurityManager中提供了CachingSecurityManager接口,内部组件Realm也有子接口CachingRealm。

shiro内部的缓存管理结构:

Apache Shiro安全框架(5)-会话管理和缓存_第4张图片

但是更多的是实现CacheManager接口,实现其中的getCache()方法,然后在创建Cache的实现类,getCche()方法返回该实现类的实例

以下演示利用redis自定义缓存管理:

Cache实现类:

import com.heyu.framework.service.RedisService;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Collection;
import java.util.Set;

public class RedisCache implements Cache {

    @Autowired
    private RedisService redisService;

    private String cacheKey;

    private static Long expire = 3600L;

    private static final String PREFIX = "SHIRO_CACHE";

    public RedisCache(String name,RedisService redisService){
        this.redisService = redisService;
        this.cacheKey = PREFIX + name;
    }

    public K getCacheKey(Object k) {
        return (K)(this.cacheKey+k);
    }

    public void setCacheKey(String cacheKey) {
        this.cacheKey = cacheKey;
    }

    @Override
    public V get(K k) throws CacheException {
        return (V) redisService.get(getCacheKey(k));
    }

    @Override
    public V put(K k, V v) throws CacheException {
        redisService.set(getCacheKey(k),v,expire);
        return v;
    }

    @Override
    public V remove(K k) throws CacheException {
        V v = (V) redisService.get(getCacheKey(k));
        redisService.delete(getCacheKey(k));
        return v;
    }

    @Override
    public void clear() throws CacheException {

    }

    @Override
    public int size() {
        return 0;
    }

    @Override
    public Set keys() {
        return null;
    }

    @Override
    public Collection values() {
        return null;
    }
}

redis操作类RedisSerivce:

import com.heyu.framework.exception.CommonException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class RedisService {

    private RedisTemplate redisTemplate;

    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisSerializer redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        this.redisTemplate = redisTemplate;
    }

    /**
     * 将数据存入缓存
     * @param key
     * @param value
     * @return
     */
    public boolean set(K key,V value){
        boolean result = false;
        try {
            ValueOperations operations = redisTemplate.opsForValue();
            operations.set(key,value);
            result = true;

        }catch (Exception e){
            e.printStackTrace();
            throw new CommonException(e.getMessage());
        }
        return  result;
    }

    /**
     * 将数据存入缓存,并设置有效期,单位为秒
     * @param key
     * @param value
     * @param expire
     * @return
     */
    public boolean set(K key,V value,Long expire){
        boolean result = false;
        try {
            ValueOperations operations = redisTemplate.opsForValue();
            operations.set(key,value,expire,TimeUnit.SECONDS);
            result = true;

        }catch (Exception e){
            e.printStackTrace();
            throw new CommonException(e.getMessage());
        }
        return  result;
    }

    /**
     * 从缓存中获取数据
     * @param key
     * @return
     */
    public Object get(K key){
        Object result = null;
        ValueOperations operations = redisTemplate.opsForValue();
        result = operations.get(key);
        return result;
    }

    /**
     * 根据key从缓存中删除该key-value
     * @param key
     */
    public void delete(K key){
        if(exists(key)){
            redisTemplate.delete(key);
        }
    }

    /**
     * 批量删除
     * @param pattern
     */
    public void deletePattern(K pattern){
        Set keys = redisTemplate.keys(pattern);
        if (keys != null && keys.size() > 0){
            redisTemplate.delete(keys);
        }
    }
    /**
     * 判断缓存中 是否存在该key
     * @param key
     * @return
     */
    public boolean exists(K key){
        return redisTemplate.hasKey(key);
    }

    public void expire(K key,Long expireTime,TimeUnit unit){
        redisTemplate.expire(key,expireTime,unit);
    }
}

CacheManager实现类:

import com.heyu.framework.service.RedisService;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 自定义shiro中的缓存管理器,使用redis缓存
 */
public class RedisCacheManager implements CacheManager {

    @Autowired
    private RedisService redisService;

    @Override
    public  Cache getCache(String name) throws CacheException {
        return new RedisCache<>(name,redisService);
    }

    public RedisService getRedisService() {
        return redisService;
    }

    public void setRedisService(RedisService redisService) {
        this.redisService = redisService;
    }
}

以上就完成了shiro中缓存的自定义,接下来只需实例化SecurityManager中的CacheManager属性:

@Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //内部方法调用realm的getAuthenticationInfo获取认证信息
        securityManager.setRealm(authRealm());
        securityManager.setCacheManager(redisCacheManager());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }
@Bean
    public RedisCacheManager redisCacheManager(){
        RedisCacheManager cacheManager = new RedisCacheManager();
        return cacheManager;
    }


你可能感兴趣的:(Java)