最近看到网上有其他文章讲述shiro比较齐全,记录一下跟我一起学shiro
Session Management
shiro可以为单机应用到集群应用提供会话管理。
shiro可以在任意应用中使用,不论是不是WEB应用,也不管容器是什么。
使用shiro的会话支持 ,主要是shiro有如下特性:
- 基于POJO/J2SE(IOC友好) 所有内容都基于接口,并与POJO一起实现。可以轻松使用SpringXML、JSON或YAML进行配置;
- 轻松定义session的存储 这意味着你可以把缓存信息存放在网络分布式缓存、文件系统、关系型数据库或专有的数据存储中;
- 不同的客户端访问 可以在不同的客户端之间进行共享缓存;
- 事件监听 在session的生命周期中监听周期事件;
- 保留主机ip地址 有助于确定用户的位置,并作出相应的反映;
- 可以工作touch()延长session;
- 透明地使用WEB应用的session;
- 可以使用到SSO 因为session可以存放在数据库中,并通过应用共享;并且它用于登录,因为通过session可以保持登录状态。
使用Sessions
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
session.setAttribute("someKey",someVame);
使用方式和HttpServletRequset API一样;
另外Subject.getSession(boolean create)
和HttpServletRequest.getSession(boolean create)
的结构也是一样的.
如果已经有session,忽略传的参数.
如果没有session,如果create是true,创建session并返回;如果create是false,将不会创建,返回null;
上述方法可以应用于所有程序,甚至不是web程序.
The SessionManager
像其他的shiro框架组件组件,sessionManager需要被定义在SecurityManager内,作为最高等级的组件;
可以通过getter和setter方法注入到SecurityManager中并使用.
SessionManager开箱即用的实现不需要我们做什么,并且它高度可定制.
SessionTimeout
默认的过期时间是30分钟.我们可以设置globalSessionTimeout
自定义过期时间.时间的单位是毫秒而不是秒.
Session 监听
Shiro支持SessionListener
,对重要的sessin时间做出反应。我们可以实现SessionListener
接口或者继承SessionListenerAdapter
来实现监听.
需要配置在SessionManager
的sessionListeners属性上.该属性是一个集合.
SessionListeners
只会被特定的session通知,不会被所有的session通知.
Session Storage(会话的存储)
SessionDao
用来反映数据存储的设计模型,需要设置在SessionManager中;
SessionDao
不用自己去定义,我们可以使用shiro定义好的.
web应用下默认情况下sessionManager是servlet默认的session manager,不支持存储sessionDao.如果我们需要使用sessionDao,需要先定义SessionManager;
EHCache SessionDao
EHCache
默认是不使用的,如果你不计划自己定义SessionDao
,建议使用EHCaChe.支持把session存放在磁盘而不是内存中.
引入shiro-ehcache-<version>.jar
默认的使用一个Shiro的一个特定的ehcache.xml;
Custom Session IDs
Shio的SessionDao使用内部的SessionIdGenerator接口的组件,为session生成Sessionid,默认的是JavaUUUidSessionIdGenerator;
如果不能满足需求,可以实现SessionIdGenerator
接口,并配置在SessionDao中.
Session Validation & Scheduling
session需要被验证,如果过期则从存储中删除.当使用subject.getSession()
时,shiro将会去验证.
这意味着,如果不给一个定期的验证,session就不会删除.
因此SessionManager支持SessionValidationScheduler
的概念,它负责周期地去验证,以便清理.
Default SessionValidationScheduler
默认的SessionValidationScheduler
是ExecutorServiceSessionValidationScheduler
,它使用JDK的ScheduledExecutorService
去控制验证.每一小时验证一次.你可以重新创建一个ExecutorServiceSessionValidationScheduler
实例去改变它,单位是毫秒.
Custom SessionValidationScheduler
你可以自定义一个SessionValidationScheduler
,并配置给SessionDao.
Disabling Session Validation
如果不使用Shiro的验证.例如你使用shiro外部的验证.
需要设置SessionDao
的sessionValidationSchedulerEnabled
属性为false;
如果关闭,则必须保证周期验证.
Invalid Session Deletion
默认情况下,删除session是shiro进行删除的.如果要禁用的话,可以设置SessionManager.deleteInvalidSessions = false
.但这时你要负责自己删除session;
配置文件
<property name="sessionManager">
<bean class="com.qunar.lfz.shiro.MySessionManager">
<!--默认自动检查session过期,删除过期session-->
<property name="globalSessionTimeout" value="1800000"/>
<property name="sessionValidationInterval" value="1800000"/>
<!--默认MemorySessionDao-->
<property name="sessionDAO">
<!--默认使用javaUuidGenerator-->
<bean class="com.qunar.lfz.shiro.MyRedisSessionDao"/>
</property>
<!-- 因为shiro生成的cookieid是JSESSIONID,此处是修改一下默认值 -->
<property name="sessionIdCookie">
<bean class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="shiroCookie"/>
</bean>
</property>
<!--避免url中出现jsessionid // 去掉shiro登录时url里的JSESSIONID-->
<property name="sessionIdUrlRewritingEnabled" value="false"/>
<!--默认使用ExecutorServiceSessionValidationScheduler,AbstractValidatingSessionManager:209-->
</bean>
</property>
定义sessionDao
public class MyRedisSessionDao extends AbstractSessionDAO{
//redis中session过期时间
private static final int SESSION_EXPIRE = 60 * 30;
@Resource
private RedisClient client;
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
assignSessionId(session,sessionId);
byte[] keyByte = RedisKey.getKeyByte(RedisKey.SESSION_PRE, sessionId.toString());
byte[] sessionByte = SerializationUtils.serialize(session);
client.setEx(keyByte, sessionByte, SESSION_EXPIRE);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
if (sessionId == null) {
return null;
}
byte[] keyByte = RedisKey.getKeyByte(RedisKey.SESSION_PRE, sessionId.toString());
byte[] sessionByte = client.get(keyByte);
return (Session) SerializationUtils.deserialize(sessionByte);
}
@Override
public void update(Session session) throws UnknownSessionException {
byte[] keyByte = RedisKey.getKeyByte(RedisKey.SESSION_PRE, session.getId().toString());
byte[] sessionByte = SerializationUtils.serialize(session);
client.setEx(keyByte, sessionByte, SESSION_EXPIRE);
}
@Override
public void delete(Session session) {
byte[] keyByte = RedisKey.getKeyByte(RedisKey.SESSION_PRE, session.getId().toString());
client.delete(keyByte);
}
@Override
public Collection<Session> getActiveSessions() {
Set<byte[]> keyByteSet = client.keys((RedisKey.SESSION_PRE + "*").getBytes());
Set<Session> sessionSet = Sets.newHashSet();
for (byte[] keyByte : keyByteSet) {
byte[] sessionByte = client.get(keyByte);
Session session = (Session) SerializationUtils.deserialize(sessionByte);
if (session != null) {
sessionSet.add(session);
}
}
return sessionSet;
}
}
定义remember me
<property name="rememberMeManager">
<bean class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cookie">
<bean class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="maxAge" value="1800"/>
</bean>
</property>
</bean>
</property>
使用注解
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<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>