1.session
使用token登录之后可以直接使用subject获取用户session。可以操作session
- public void test(){
- Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
- org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
- SecurityUtils.setSecurityManager(securityManager);
- Subject subject = SecurityUtils.getSubject();
- UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");//这个用户在ini文件中存在,shiro自动做密码和用户名匹配验证
- //登录
- subject.login(token);
- //获取用户session(如果当前用户没有常见session的话,true则创建一个并返回,false为返回null)
- Session session = subject.getSession(true);
- //主机
- System.out.println("host:"+session.getHost());
- //session超时时间
- session.setTimeout(1500000);
- System.out.println("timeout:"+session.getTimeout());
- //属性参数值
- session.setAttribute("name", subject.getPrincipal());
- System.out.println("name:"+session.getAttribute("name"));
- session.removeAttribute("name");
- System.out.println("attributekeys:"+session.getAttributeKeys());
- //id
- System.out.println("id:"+session.getId());
- //访问时间(创建session的时间和最后访问session的时间)
- System.out.println("lastAccessTime:"+session.getLastAccessTime());
- session.touch();//更新会话访问时间
- System.out.println("StartTimestamp:"+session.getStartTimestamp());
- //销毁session
- session.stop();
- }
2.session管理器
session管理器用来管理所有用户的subject的session。包括创建、维护、删除、失效、验证。
Shiro提供了三个默认实现:
(1)DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于JavaSE环境;
- [main]
- sessionManager=org.apache.shiro.session.mgt.DefaultSessionManager
- securityManager.sessionManager=$sessionManager
(2)ServletContainerSessionManager:DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话;
shiro.ini:
- [main]
- sessionManager=org.apache.shiro.web.session.mgt.ServletContainerSessionManager
- securityManager.sessionManager=$sessionManager
- #设置全局session过期时间,默认30分钟
- sessionManager. globalSessionTimeout=1800000
- [users] zhang=123 wang=123
这个session管理器使用了servlet,所以可以在web.xml文件中设置session超时时间
web.xml:- <session-config>
- <session-timeout>30</session-timeout>
- </session-config>
(3)DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager,自己维护着会话,直接废弃了Servlet容器的会话管理。
shiro.ini:
- [main]
- #sessionManager创建会话Cookie的模板
- sessionIdCookie=org.apache.shiro.web.servlet.SimpleCookie
- #session管理器
- sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
- #设置Cookie名字,默认为JSESSIONID
- sessionIdCookie.name=sid
- #设置Cookie的域名,默认空,即当前访问的域名
- #sessionIdCookie.domain=sishuok.com
- #设置Cookie的路径,默认空,即存储在域名根下
- #sessionIdCookie.path=
- #设置Cookie的过期时间,秒为单位,默认-1表示关闭浏览器时过期
- sessionIdCookie.maxAge=1800
- #如果设置为true,则客户端不会暴露给客户端脚本代码
- #使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击
- #此特性需要实现了Servlet 2.5 MR6及以上版本的规范的Servlet
- sessionIdCookie.httpOnly=true
- #是否启用/禁用Session Id Cookie,默认是启用的,
- #如果禁用后将不会设置Session Id Cookie
- #即默认使用了Servlet容器的JSESSIONID
- #且通过URL重写(URL中的“;JSESSIONID=id”部分)保存Session Id
- sessionManager.sessionIdCookieEnabled=true
- sessionManager.sessionIdCookie=$sessionIdCookie
- securityManager.sessionManager=$sessionManager
- [users]
- zhang=123
- wang=123
3.session监听器
- [main]
- sessionListener=com.tyf.www.MySessionListener
- sessionManager.sessionListeners=$sessionListener
- [users]
- zhang=123
- wang=123
(1)监听会话创建、过期、停止
- public class MySessionListener implements SessionListener {
- @Override
- public void onStart(Session session) {//会话创建时触发
- System.out.println("会话创建:" + session.getId());
- }
- @Override
- public void onExpiration(Session session) {//会话过期时触发
- System.out.println("会话过期:" + session.getId());
- }
- @Override
- public void onStop(Session session) {//退出/会话过期时触发
- System.out.println("会话停止:" + session.getId());
- }
- }
(2)监听会话的某个事件
- public class MySessionListener extends SessionListenerAdapter {
- @Override
- public void onStart(Session session) {
- System.out.println("会话创建:" + session.getId());
- }
- }
4.session持久化
encache依赖:
- <!-- encache -->
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-ehcache</artifactId>
- <version>1.2.3</version>
- </dependency>
shiro封装了sessionDao用于对sesson的增删改查,并且采用ehcache缓存作为存储方式(ehcache+terracotta可以实现session容器无关的存储集群)
shiro.ini:
- [main]
- #设置sessionDao实现类
- sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
- #设置Session缓存名字,默认就是shiro-activeSessionCache
- sessionDAO.activeSessionsCacheName=shiro-activeSessionCache
- #缓存管理器,用于管理缓存的,此处使用Ehcache实现
- cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
- #设置ehcache缓存的配置文件
- cacheManager.cacheManagerConfigFile=classpath:ehcache.xml
- sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
- sessionManager.sessionDAO=$sessionDAOsecurityManager.cacheManager = $cacheManager
- [users] zhang=123 wang=123
同时可以设置sessionid生成方式:
- sessionIdGenerator=org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator
- sessionDAO.sessionIdGenerator=$sessionIdGenerator
ehcache.xml :
- <?xml version="1.0" encoding="UTF-8"?>
- <ehcache name="es">
- <diskStore path="java.io.tmpdir"/>
- <cache name="shiro-activeSessionCache"
- maxEntriesLocalHeap="10000"
- overflowToDisk="false"
- eternal="false"
- diskPersistent="false"
- timeToLiveSeconds="0"
- timeToIdleSeconds="0"
- statistics="true"/>
- </ehcache>
(4)自定义sessionDao:
自己实现一个sessdao将session序列化进mysql,在通过获取session时会先到ehcahce中查找之后再到mysql中查找
依赖:
创建session持久化数据库:
- drop table if exists sessions;
- create table sessions (
- id varchar(200),
- session varchar(2000),
- constraint pk_sessions primary key(id)
- ) charset=utf8 ENGINE=InnoDB;
序列化工具类:
- import org.apache.shiro.codec.Base64;
- import org.apache.shiro.session.Session;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- /*
- *
- * session序列化工具类
- *
- *
- */
- public class SerializableUtils {
- //序列化
- public static String serialize(Session session) {
- try {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(bos);
- oos.writeObject(session);
- return Base64.encodeToString(bos.toByteArray());
- } catch (Exception e) {
- throw new RuntimeException("serialize session error", e);
- }
- }
- //反序列化
- public static Session deserialize(String sessionStr) {
- try {
- ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(sessionStr));
- ObjectInputStream ois = new ObjectInputStream(bis);
- return (Session)ois.readObject();
- } catch (Exception e) {
- throw new RuntimeException("deserialize session error", e);
- }
- }
- }
jdbc工具类:
- import com.alibaba.druid.pool.DruidDataSource;
- import org.springframework.jdbc.core.JdbcTemplate;
- /*
- *
- * jdbc工具类
- *
- *
- */
- public class JdbcTemplateUtils {
- private static JdbcTemplate jdbcTemplate;
- public static JdbcTemplate jdbcTemplate() {
- if(jdbcTemplate == null) {
- jdbcTemplate = createJdbcTemplate();
- }
- return jdbcTemplate;
- }
- private static JdbcTemplate createJdbcTemplate() {
- DruidDataSource ds = new DruidDataSource();
- ds.setDriverClassName("com.mysql.jdbc.Driver");
- ds.setUrl("jdbc:mysql://localhost:3306/shiro");
- ds.setUsername("root");
- ds.setPassword("1234");
- return new JdbcTemplate(ds);
- }
- }
sessionDao:
- import org.apache.shiro.session.Session;
- import org.apache.shiro.session.mgt.ValidatingSession;
- import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
- import org.springframework.jdbc.core.JdbcTemplate;
- import java.io.Serializable;
- import java.util.List;
- /*
- * sessionDao:继承了CachingSessionDao意味着会先查找缓存,再通过下面的方法查找数据库
- *
- *
- */
- public class MySessionDAO extends CachingSessionDAO {
- private JdbcTemplate jdbcTemplate = JdbcTemplateUtils.jdbcTemplate();
- //在创建完session之后会调用
- @Override
- protected Serializable doCreate(Session session) {
- System.out.println("doCreate");
- Serializable sessionId = generateSessionId(session);
- assignSessionId(session, sessionId);
- String sql = "insert into sessions(id, session) values(?,?)";
- jdbcTemplate.update(sql, sessionId, SerializableUtils.serialize(session));
- return session.getId();
- }
- //更新session最后访问时间、停止session、设置超时时间、移除属性值时会调用
- @Override
- protected void doUpdate(Session session) {
- System.out.println("doUpdate");
- if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid()) {
- return; //如果会话过期/停止 没必要再更新了
- }
- String sql = "update sessions set session=? where id=?";
- jdbcTemplate.update(sql, SerializableUtils.serialize(session), session.getId());
- }
- //用户logout、session过期会调用
- @Override
- protected void doDelete(Session session) {
- System.out.println("doDelete");
- String sql = "delete from sessions where id=?";
- jdbcTemplate.update(sql, session.getId());
- }
- @Override
- protected Session doReadSession(Serializable sessionId) {
- String sql = "select session from sessions where id=?";
- List<String> sessionStrList = jdbcTemplate.queryForList(sql, String.class, sessionId);
- if(sessionStrList.size() == 0) return null;
- return SerializableUtils.deserialize(sessionStrList.get(0));
- }
- }
shiro.ini:
- [main]
- #设置sessionDao实现类
- sessionDAO=com.tyf.www.MySessionDAO
- #设置Session缓存名字,默认就是shiro-activeSessionCache
- sessionDAO.activeSessionsCacheName=shiro-activeSessionCache
- #缓存管理器,用于管理缓存的,此处使用Ehcache实现
- cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
- #设置ehcache缓存的配置文件
- cacheManager.cacheManagerConfigFile=classpath:ehcache.xml
- sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
- sessionManager.sessionDAO=$sessionDAO
- securityManager.cacheManager = $cacheManager
- securityManager.sessionManager=$sessionManager
- [users]
- zhang=123
- wang=123
ehcache.xml :
- <?xml version="1.0" encoding="UTF-8"?>
- <ehcache name="es">
- <diskStore path="java.io.tmpdir"/>
- <cache name="shiro-activeSessionCache"
- maxEntriesLocalHeap="10000"
- overflowToDisk="false"
- eternal="false"
- diskPersistent="false"
- timeToLiveSeconds="0"
- timeToIdleSeconds="0"
- statistics="true"/>
- </ehcache>
web.xml:
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
- id="WebApp_ID" version="3.0">
- <display-name>Archetype Created Web Application</display-name>
- <!--- EnvironmentLoaderListener用来生成SecurityManager -->
- <listener>
- <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
- </listener>
- <context-param>
- <param-name>shiroEnvironmentClass</param-name>
- <param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value><!-- 默认先从/WEB-INF/shiro.ini,如果没有找classpath:shiro.ini -->
- </context-param>
- <context-param>
- <param-name>shiroConfigLocations</param-name>
- <param-value>classpath:shiro.ini</param-value><!-- 指定shiro配置文件路径 -->
- </context-param>
- <filter>
- <filter-name>shiroFilter</filter-name>
- <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>shiroFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- </web-app>
测试类:
- public void test(){
- Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
- org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
- SecurityUtils.setSecurityManager(securityManager);
- Subject subject = SecurityUtils.getSubject();
- UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");//这个用户在ini文件中存在,shiro自动做密码和用户名匹配验证
- //登录
- subject.login(token);
- //获取用户session
- Session session = subject.getSession(true);
- System.out.println("id:"+session.getId());
- //用户退出(还没有删除session)
- subject.logout();
- //销毁session
- session.stop();
- }
分别创建session、打印sessioni_d、销毁session。查看数据库中session记录:
5.session验证
web环境中用户不主动退出,shiro不知道session是否已经过期,所以shiro提供一个验证器来定期验证session是否过期
- sessionValidationScheduler=org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler
- sessionValidationScheduler.interval = 3600000
- sessionValidationScheduler.sessionManager=$sessionManager
- sessionManager.globalSessionTimeout=1800000
- sessionManager.sessionValidationSchedulerEnabled=true
- sessionManager.sessionValidationScheduler=$sessionValidationScheduler
sessionValidationScheduler:会话验证调度器,sessionManager默认就是使用ExecutorServiceSessionValidationScheduler,其使用JDK的ScheduledExecutorService进行定期调度并验证会话是否过期;
sessionValidationScheduler.interval:设置调度时间间隔,单位毫秒,默认就是1小时;
sessionValidationScheduler.sessionManager:设置会话验证调度器进行会话验证时的会话管理器;
sessionManager.globalSessionTimeout:设置全局会话超时时间,默认30分钟,即如果30分钟内没有访问会话将过期;
sessionManager.sessionValidationSchedulerEnabled:是否开启会话验证器,默认是开启的;
sessionManager.sessionValidationScheduler:设置会话验证调度器,默认就是使用ExecutorServiceSessionValidationScheduler。
6.sessionFactory
sessionFactory是创建会话的工厂,根据相应的Subject上下文信息来创建会话;默认提供了SimpleSessionFactory用来创建SimpleSession会话
自定义session :
- public class OnlineSession extends SimpleSession {
- public static enum OnlineStatus {
- on_line("在线"), hidden("隐身"), force_logout("强制退出");
- private final String info;
- private OnlineStatus(String info) {
- this.info = info;
- }
- public String getInfo() {
- return info;
- }
- }
- private String userAgent; //用户浏览器类型
- private OnlineStatus status = OnlineStatus.on_line; //在线状态
- private String systemHost; //用户登录时系统IP
- //省略其他
- }
自定义sessionFactory :
- public class OnlineSessionFactory implements SessionFactory {
- @Override
- public Session createSession(SessionContext initData) {
- OnlineSession session = new OnlineSession();
- if (initData != null && initData instanceof WebSessionContext) {
- WebSessionContext sessionContext = (WebSessionContext) initData;
- HttpServletRequest request = (HttpServletRequest) sessionContext.getServletRequest();
- if (request != null) {
- session.setHost(IpUtils.getIpAddr(request));
- session.setUserAgent(request.getHeader("User-Agent"));
- session.setSystemHost(request.getLocalAddr() + ":" + request.getLocalPort());
- }
- }
- return session;
- }
- }
shiro.ini :
- sessionFactory=org.apache.shiro.session.mgt.OnlineSessionFactory
- sessionManager.sessionFactory=$sessionFactory