会话,即用户保持和服务端之间的联系,保证用户在下一次访问服务端时不必在提交用户身份信息。而服务端可以通过用户提交的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中会话管理的结构图如下:
发现SecurityManager继承了SessionManager接口,而SecurityManager结构图如下:
发现SessionSecurityManager实现了接口SecurityManager,实质SessionSecurityManager实现了把会话管理委托给对应的SessionManager。而DefaultSecurityManager和DefaultWebSecurityManager都继承了SessionSecurityManager
自定义类实现SessionListener接口,并实现其中的方法。SessionListener方法如下:
shiro中SessionDao的结构图
以上是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内部的缓存管理结构:
但是更多的是实现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;
}