相关阅读
简介
会话管理器,管理会话的创建、维护和清理;
核心方法
/**
* 根据上下文初始化数据创建新会话
*/
Session start(SessionContext context);
/**
* 根据会话ID查找会话
* 如果找到但会话无效(停止或过期),则抛出SessionException
*/
Session getSession(SessionKey key) throws SessionException;
实现子类
public interface SessionManager
public abstract class AbstractSessionManager implements SessionManager
public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager, EventBusAware
public abstract class AbstractValidatingSessionManager extends AbstractNativeSessionManager implements ValidatingSessionManager, Destroyable
public class DefaultSessionManager extends AbstractValidatingSessionManager implements CacheManagerAware
public class DefaultWebSessionManager extends DefaultSessionManager implements WebSessionManager
public interface NativeSessionManager extends SessionManager
public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager, EventBusAware
public abstract class AbstractValidatingSessionManager extends AbstractNativeSessionManager implements ValidatingSessionManager, Destroyable
public class DefaultSessionManager extends AbstractValidatingSessionManager implements CacheManagerAware
public class DefaultWebSessionManager extends DefaultSessionManager implements WebSessionManager
public interface SecurityManager extends Authenticator, Authorizer, SessionManager
public interface ValidatingSessionManager extends SessionManager
public abstract class AbstractValidatingSessionManager extends AbstractNativeSessionManager implements ValidatingSessionManager, Destroyable
public class DefaultSessionManager extends AbstractValidatingSessionManager implements CacheManagerAware
public class DefaultWebSessionManager extends DefaultSessionManager implements WebSessionManager
public interface WebSessionManager extends SessionManager
public class DefaultWebSessionManager extends DefaultSessionManager implements WebSessionManager
public class ServletContainerSessionManager implements WebSessionManager
AbstractSessionManager
简介
支持全局超时时间配置,默认全局超时时间为30M;
核心方法
// 默认全局超时时间
public static final long DEFAULT_GLOBAL_SESSION_TIMEOUT = 30 * MILLIS_PER_MINUTE;
// 全局超时时间
private long globalSessionTimeout = DEFAULT_GLOBAL_SESSION_TIMEOUT;
/**
* 获取全局超时时间
*/
public long getGlobalSessionTimeout() {
return this.globalSessionTimeout;
}
/**
* 设置全局超时时间
*/
public void setGlobalSessionTimeout(long globalSessionTimeout) {
this.globalSessionTimeout = globalSessionTimeout;
}
NativeSessionManager
简介
直接负责Session
实例的创建、维护、清理和实例的生命周期管理;
核心方法
/**
* 获取指定会话创建时间
*/
Date getStartTimestamp(SessionKey key);
/**
* 获取指定会话最近一次访问时间
*/
Date getLastAccessTime(SessionKey key);
/**
* 指定会话是否有效
*/
boolean isValid(SessionKey key);
/**
* 校验指定会话是否有效
* 无效则抛出InvalidSessionException
*/
void checkValid(SessionKey key) throws InvalidSessionException;
/**
* 获取指定会话的超时时间
*/
long getTimeout(SessionKey key) throws InvalidSessionException;
/**
* 设置指定会话的超时时间
*/
void setTimeout(SessionKey key, long maxIdleTimeInMillis) throws InvalidSessionException;
/**
* 显示更新指定会话的最近一次访问时间为当前时间
* 避免会话超时
*/
void touch(SessionKey key) throws InvalidSessionException;
/**
* 获取指定会话创建时所在主机的hostname/ip
*/
String getHost(SessionKey key);
/**
* 停止指定会话
*/
void stop(SessionKey key) throws InvalidSessionException;
/**
* 获取指定会话的所有属性的键
*/
Collection<Object> getAttributeKeys(SessionKey sessionKey);
/**
* 获取指定会话的指定属性
*/
Object getAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException;
/**
* 设置指定会话的指定属性
*/
void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException;
/**
* 移除指定会话的指定属性
*/
Object removeAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException;
AbstractNativeSessionManager
简介
NativeSessionManager
接口的抽象实现,支持SessionListener
和globalSessionTimeout
;
核心方法
// sessionEvent总线
private EventBus eventBus;
// sessionListener集合
private Collection<SessionListener> listeners;
/**
* 发布事件
*/
protected void publishEvent(Object event) {
if (this.eventBus != null) {
this.eventBus.publish(event);
}
}
/**
* 根据上下文初始化数据创建新会话
* 模板模式,实现算法框架,由子类实现算法细节
*/
public Session start(SessionContext context) {
Session session = createSession(context);
applyGlobalSessionTimeout(session);
onStart(session, context);
notifyStart(session);
//Don't expose the EIS-tier Session object to the client-tier:
return createExposedSession(session, context);
}
/**
* 根据上下文初始化数据创建新会话
* 算法细节,由子类实现
*/
protected abstract Session createSession(SessionContext context) throws AuthorizationException;
/**
* 应用全局超时时间
*/
protected void applyGlobalSessionTimeout(Session session) {
session.setTimeout(getGlobalSessionTimeout());
onChange(session);
}
/**
* 会话创建
* 算法细节,由子类实现
*/
protected void onStart(Session session, SessionContext context) {
}
/**
* 根据会话ID查找会话
*/
public Session getSession(SessionKey key) throws SessionException {
Session session = lookupSession(key);
return session != null ? createExposedSession(session, key) : null;
}
/**
* 根据会话ID查找会话
*/
private Session lookupSession(SessionKey key) throws SessionException {
if (key == null) {
throw new NullPointerException("SessionKey argument cannot be null.");
}
return doGetSession(key);
}
/**
* 根据会话ID查找会话,找不到则抛出UnknownSessionException异常
*/
private Session lookupRequiredSession(SessionKey key) throws SessionException {
Session session = lookupSession(key);
if (session == null) {
String msg = "Unable to locate required Session instance based on SessionKey [" + key + "].";
throw new UnknownSessionException(msg);
}
return session;
}
/**
* 根据会话ID查找会话
* 算法细节,由子类实现算法细节
*/
protected abstract Session doGetSession(SessionKey key) throws InvalidSessionException;
/**
* 创建对外的session
*/
protected Session createExposedSession(Session session, SessionContext context) {
return new DelegatingSession(this, new DefaultSessionKey(session.getId()));
}
/**
* 创建对外的session
*/
protected Session createExposedSession(Session session, SessionKey key) {
return new DelegatingSession(this, new DefaultSessionKey(session.getId()));
}
/**
* 通知指定session无效之前的处理
*/
protected Session beforeInvalidNotification(Session session) {
// 包装session为不可变
return new ImmutableProxiedSession(session);
}
/**
* 通知指定session开始
*/
protected void notifyStart(Session session) {
// 遍历sessionListener集合
for (SessionListener listener : this.listeners) {
listener.onStart(session);
}
}
/**
* 通知指定session停止
*/
protected void notifyStop(Session session) {
// 包装session,防止sessionListener修改session
Session forNotification = beforeInvalidNotification(session);
for (SessionListener listener : this.listeners) {
listener.onStop(forNotification);
}
}
/**
* 通知指定session过期
*/
protected void notifyExpiration(Session session) {
// 包装session,防止sessionListener修改session
Session forNotification = beforeInvalidNotification(session);
for (SessionListener listener : this.listeners) {
listener.onExpiration(forNotification);
}
}
/**
* 获取指定会话创建时间
*/
public Date getStartTimestamp(SessionKey key) {
return lookupRequiredSession(key).getStartTimestamp();
}
/**
* 获取指定会话最近一次访问时间
*/
public Date getLastAccessTime(SessionKey key) {
return lookupRequiredSession(key).getLastAccessTime();
}
/**
* 获取指定会话的超时时间
*/
public long getTimeout(SessionKey key) throws InvalidSessionException {
return lookupRequiredSession(key).getTimeout();
}
/**
* 设置指定会话的超时时间
*/
public void setTimeout(SessionKey key, long maxIdleTimeInMillis) throws InvalidSessionException {
Session s = lookupRequiredSession(key);
s.setTimeout(maxIdleTimeInMillis);
onChange(s);
}
/**
* 显式更新指定会话的最近一次访问时间为当前时间
*/
public void touch(SessionKey key) throws InvalidSessionException {
Session s = lookupRequiredSession(key);
s.touch();
onChange(s);
}
/**
* 获取指定会话创建时所在主机的hostname/ip
*/
public String getHost(SessionKey key) {
return lookupRequiredSession(key).getHost();
}
/**
* 获取指定会话的所有属性的键
*/
public Collection<Object> getAttributeKeys(SessionKey key) {
Collection<Object> c = lookupRequiredSession(key).getAttributeKeys();
if (!CollectionUtils.isEmpty(c)) {
return Collections.unmodifiableCollection(c);
}
return Collections.emptySet();
}
/**
* 获取指定会话的指定属性
*/
public Object getAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException {
return lookupRequiredSession(sessionKey).getAttribute(attributeKey);
}
/**
* 设置指定会话的指定属性
*/
public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException {
if (value == null) {
removeAttribute(sessionKey, attributeKey);
} else {
Session s = lookupRequiredSession(sessionKey);
s.setAttribute(attributeKey, value);
onChange(s);
}
}
/**
* 移除指定会话的指定属性
*/
public Object removeAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException {
Session s = lookupRequiredSession(sessionKey);
Object removed = s.removeAttribute(attributeKey);
if (removed != null) {
onChange(s);
}
return removed;
}
/**
* 指定会话是否有效
*/
public boolean isValid(SessionKey key) {
try {
checkValid(key);
return true;
} catch (InvalidSessionException e) {
return false;
}
}
/**
* 停止指定会话
*/
public void stop(SessionKey key) throws InvalidSessionException {
Session session = lookupRequiredSession(key);
try {
if (log.isDebugEnabled()) {
log.debug("Stopping session with id [" + session.getId() + "]");
}
session.stop();
onStop(session, key);
notifyStop(session);
} finally {
afterStopped(session);
}
}
/**
* 会话停止
* 模板方法,由子类实现额外处理
*/
protected void onStop(Session session, SessionKey key) {
onStop(session);
}
/**
* 会话停止
* 模板方法,由子类实现额外处理
*/
protected void onStop(Session session) {
onChange(session);
}
/**
* 会话停止后处理
* 算法细节,由子类实现
*/
protected void afterStopped(Session session) {
}
/**
* 校验指定会话是否有效
* 无效则抛出InvalidSessionException
*/
public void checkValid(SessionKey key) throws InvalidSessionException {
//just try to acquire it. If there is a problem, an exception will be thrown:
lookupRequiredSession(key);
}
/**
* 会话变化
* 算法细节,由子类实现
*/
protected void onChange(Session s) {
}
ValidatingSessionManager
简介
对系统中所有打开会话(尚未停止或过期)执行会话验证,如果会话无效,则将其更新并保存到EIS;
此方法应定期运行,最佳频率将由应用程序基于性能、平均活跃用户数、最少活动时间等因素决定;
大多数应用程序根据HTTP协议采用请求/响应编程模型,服务仅在响应客户端请求或者方法调用时工作,安全系统只需在这些情况下验证会话;
但如果客户端忘记注销,或者服务出现故障情况下,会话可能被孤立,因为没有进一步的请求使用该会话;
由于这些较低概率的情况,可能需要定期清理系统维护的孤立会话;
如果想要主动验证单个会话,可以使用getSession(SessionKey)方法;
核心方法
/**
* 校验系统中所有活动的会话
* 定期执行,支持SessionValidationScheduler
*/
void validateSessions();
AbstractValidatingSessionManager
简介
ValidatingSessionManager
接口的抽象实现;
核心方法
// 默认会话校验间隔为1小时
public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = MILLIS_PER_HOUR;
// 启用会话校验调度器标识
protected boolean sessionValidationSchedulerEnabled;
// 会话校验调度器
protected SessionValidationScheduler sessionValidationScheduler;
// 会话校验间隔
protected long sessionValidationInterval;
/**
* 构造方法
*/
public AbstractValidatingSessionManager() {
// 默认开启会话校验调度器
this.sessionValidationSchedulerEnabled = true;
// 默认会话校验间隔为1小时
this.sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
}
/**
* 如有必要开启会话校验
*/
private void enableSessionValidationIfNecessary() {
SessionValidationScheduler scheduler = getSessionValidationScheduler();
if (isSessionValidationSchedulerEnabled() && (scheduler == null || !scheduler.isEnabled())) {
// 开启会话检验
enableSessionValidation();
}
}
/**
* 根据会话ID查找会话
* 算法模板,由子类实现算法细节
*/
@Override
protected final Session doGetSession(final SessionKey key) throws InvalidSessionException {
enableSessionValidationIfNecessary();
log.trace("Attempting to retrieve session with key {}", key);
// 检索session
Session s = retrieveSession(key);
if (s != null) {
// 校验session
validate(s, key);
}
// 校验通过后返回session
return s;
}
/**
* 根据会话键查找会话
* 算法细节,由子类实现
*/
protected abstract Session retrieveSession(SessionKey key) throws UnknownSessionException;
/**
* 根据上下文初始化数据创建新会话
* 模板模式,实现算法框架,由子类实现算法细节
*/
protected Session createSession(SessionContext context) throws AuthorizationException {
enableSessionValidationIfNecessary();
return doCreateSession(context);
}
/**
* 根据上下文初始化数据创建新会话
* 算法细节,由子类实现
*/
protected abstract Session doCreateSession(SessionContext initData) throws AuthorizationException;
/**
* 校验session有效性
*/
protected void validate(Session session, SessionKey key) throws InvalidSessionException {
try {
doValidate(session);
} catch (ExpiredSessionException ese) {
onExpiration(session, ese, key);
throw ese;
} catch (InvalidSessionException ise) {
onInvalidation(session, ise, key);
throw ise;
}
}
/**
* 会话过期
*/
protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) {
log.trace("Session with id [{}] has expired.", s.getId());
try {
onExpiration(s);
notifyExpiration(s);
} finally {
afterExpired(s);
}
}
/**
* 会话过期
*/
protected void onExpiration(Session session) {
onChange(session);
}
/**
* 会话过期后处理
* 算法细节,由子类实现
*/
protected void afterExpired(Session session) {
}
/**
* 会话失效
*/
protected void onInvalidation(Session s, InvalidSessionException ise, SessionKey key) {
if (ise instanceof ExpiredSessionException) {
onExpiration(s, (ExpiredSessionException) ise, key);
return;
}
log.trace("Session with id [{}] is invalid.", s.getId());
try {
onStop(s);
notifyStop(s);
} finally {
afterStopped(s);
}
}
/**
* 校验session有效性
*/
protected void doValidate(Session session) throws InvalidSessionException {
if (session instanceof ValidatingSession) {
((ValidatingSession) session).validate();
} else {
String msg = "The " + getClass().getName() + " implementation only supports validating " +
"Session implementations of the " + ValidatingSession.class.getName() + " interface. " +
"Please either implement this interface in your session implementation or override the " +
AbstractValidatingSessionManager.class.getName() + ".doValidate(Session) method to perform validation.";
throw new IllegalStateException(msg);
}
}
/**
* 创建会话校验调度器
*/
protected SessionValidationScheduler createSessionValidationScheduler() {
ExecutorServiceSessionValidationScheduler scheduler;
if (log.isDebugEnabled()) {
log.debug("No sessionValidationScheduler set. Attempting to create default instance.");
}
scheduler = new ExecutorServiceSessionValidationScheduler(this);
// 设置调度间隔
scheduler.setInterval(getSessionValidationInterval());
if (log.isTraceEnabled()) {
log.trace("Created default SessionValidationScheduler instance of type [" + scheduler.getClass().getName() + "].");
}
return scheduler;
}
/**
* 开启会话校验
*/
protected synchronized void enableSessionValidation() {
SessionValidationScheduler scheduler = getSessionValidationScheduler();
if (scheduler == null) {
// 创建会话校验调度器
scheduler = createSessionValidationScheduler();
setSessionValidationScheduler(scheduler);
}
// it is possible that that a scheduler was already created and set via 'setSessionValidationScheduler()'
// but would not have been enabled/started yet
if (!scheduler.isEnabled()) {
if (log.isInfoEnabled()) {
log.info("Enabling session validation scheduler...");
}
// 开启会话校验
scheduler.enableSessionValidation();
// 开启会话校验后处理
afterSessionValidationEnabled();
}
}
/**
* 开启会话校验后处理
* 算法细节,由子类实现
*/
protected void afterSessionValidationEnabled() {
}
/**
* 关闭会话校验
*/
protected synchronized void disableSessionValidation() {
// 关闭会话校验前处理
beforeSessionValidationDisabled();
SessionValidationScheduler scheduler = getSessionValidationScheduler();
if (scheduler != null) {
// 会话校验调度器存在
try {
// 关闭会话校验
scheduler.disableSessionValidation();
if (log.isInfoEnabled()) {
log.info("Disabled session validation scheduler.");
}
} catch (Exception e) {
if (log.isDebugEnabled()) {
String msg = "Unable to disable SessionValidationScheduler. Ignoring (shutting down)...";
log.debug(msg, e);
}
}
// 销毁会话校验调度器
LifecycleUtils.destroy(scheduler);
// 清空会话校验调度器信息
setSessionValidationScheduler(null);
}
}
/**
* 关闭会话校验前处理
* 算法细节,由子类实现
*/
protected void beforeSessionValidationDisabled() {
}
/**
* 销毁
*/
public void destroy() {
// 关闭会话校验
disableSessionValidation();
}
/**
* 校验系统中所有活动的会话
* 模板方法,由子类实现算法细节
*/
public void validateSessions() {
if (log.isInfoEnabled()) {
log.info("Validating all active sessions...");
}
int invalidCount = 0;
// 获取所有活动的会话
Collection<Session> activeSessions = getActiveSessions();
if (activeSessions != null && !activeSessions.isEmpty()) {
// 遍历活动会话
for (Session s : activeSessions) {
try {
//simulate a lookup key to satisfy the method signature.
//this could probably stand to be cleaned up in future versions:
SessionKey key = new DefaultSessionKey(s.getId());
// 校验会话
validate(s, key);
} catch (InvalidSessionException e) {
if (log.isDebugEnabled()) {
boolean expired = (e instanceof ExpiredSessionException);
String msg = "Invalidated session with id [" + s.getId() + "]" +
(expired ? " (expired)" : " (stopped)");
log.debug(msg);
}
invalidCount++;
}
}
}
if (log.isInfoEnabled()) {
String msg = "Finished session validation.";
if (invalidCount > 0) {
msg += " [" + invalidCount + "] sessions were stopped.";
} else {
msg += " No sessions were stopped.";
}
log.info(msg);
}
}
/**
* 获取所有活动的会话
* 算法细节,由子类实现
*/
protected abstract Collection<Session> getActiveSessions();
DefaultSessionManager
简介
ValidatingSessionManager
接口的默认业务实现;
所有Session
的CRUD操作都委托给内部的SessionDao
来实现;
核心方法
private SessionFactory sessionFactory;
protected SessionDAO sessionDAO;
private CacheManager cacheManager;
// 删除无效会话标识,如果使能,则当发现session无效时会自动清理
private boolean deleteInvalidSessions;
/**
* 构造方法
*/
public DefaultSessionManager() {
// 默认删除无效会话
this.deleteInvalidSessions = true;
// 默认使用SimpleSessionFactory
this.sessionFactory = new SimpleSessionFactory();
// 默认使用MemorySessionDao
this.sessionDAO = new MemorySessionDAO();
}
/**
* 设置sessionDao
*/
public void setSessionDAO(SessionDAO sessionDAO) {
this.sessionDAO = sessionDAO;
applyCacheManagerToSessionDAO();
}
/**
* 设置cacheManager
*/
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
applyCacheManagerToSessionDAO();
}
/**
* 应到cacheManager到sessionDao
*/
private void applyCacheManagerToSessionDAO() {
// 校验cacheManager和sessionDao
if (this.cacheManager != null && this.sessionDAO != null && this.sessionDAO instanceof CacheManagerAware) {
((CacheManagerAware) this.sessionDAO).setCacheManager(this.cacheManager);
}
}
/**
* 根据上下文初始化数据创建新会话
*/
protected Session doCreateSession(SessionContext context) {
// 创建会话实例
Session s = newSessionInstance(context);
if (log.isTraceEnabled()) {
log.trace("Creating session for host {}", s.getHost());
}
create(s);
return s;
}
/**
* sessionFactory创建会话实例
*/
protected Session newSessionInstance(SessionContext context) {
return getSessionFactory().createSession(context);
}
/**
* 持久化session
*/
protected void create(Session session) {
if (log.isDebugEnabled()) {
log.debug("Creating new EIS record for new session instance [" + session + "]");
}
sessionDAO.create(session);
}
/**
* 会话停止
*/
@Override
protected void onStop(Session session) {
if (session instanceof SimpleSession) {
SimpleSession ss = (SimpleSession) session;
Date stopTs = ss.getStopTimestamp();
ss.setLastAccessTime(stopTs);
}
onChange(session);
}
/**
* 会话停止后处理
*/
@Override
protected void afterStopped(Session session) {
if (isDeleteInvalidSessions()) {
// 自动删除无效会话
delete(session);
}
}
/**
* 会话过期
*/
protected void onExpiration(Session session) {
if (session instanceof SimpleSession) {
((SimpleSession) session).setExpired(true);
}
onChange(session);
}
/**
* 会话过期后处理
*/
@Override
protected void afterExpired(Session session) {
if (isDeleteInvalidSessions()) {
// 自动删除无效会话
delete(session);
}
}
/**
* 会话变化
*/
protected void onChange(Session session) {
sessionDAO.update(session);
}
/**
* 根据会话键查找会话
*/
protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
Serializable sessionId = getSessionId(sessionKey);
if (sessionId == null) {
log.debug("Unable to resolve session ID from SessionKey [{}]. Returning null to indicate a " +
"session could not be found.", sessionKey);
return null;
}
Session s = retrieveSessionFromDataSource(sessionId);
if (s == null) {
//session ID was provided, meaning one is expected to be found, but we couldn't find one:
String msg = "Could not find session with ID [" + sessionId + "]";
throw new UnknownSessionException(msg);
}
return s;
}
/**
* 获取session id
*/
protected Serializable getSessionId(SessionKey sessionKey) {
return sessionKey.getSessionId();
}
/**
* 从底层数据存储检索session
*/
protected Session retrieveSessionFromDataSource(Serializable sessionId) throws UnknownSessionException {
return sessionDAO.readSession(sessionId);
}
/**
* 去持久化session
*/
protected void delete(Session session) {
sessionDAO.delete(session);
}
/**
* 获取所有活动的会话
*/
protected Collection<Session> getActiveSessions() {
Collection<Session> active = sessionDAO.getActiveSessions();
return active != null ? active : Collections.<Session>emptySet();
}
WebSessionManager
简介
用于启用web应用程序的会话管理器;
核心方法
/**
* 会话是否由底层Servlet容器管理
*/
boolean isServletContainerSessions();
DefaultWebSessionManager
简介
支持web应用程序的SessionManager
接口的实现;
核心方法
// cookie
private Cookie sessionIdCookie;
// 开启cookie标识
private boolean sessionIdCookieEnabled;
// session id url rewrite标识
private boolean sessionIdUrlRewritingEnabled;
/**
* 构造方法
*/
public DefaultWebSessionManager() {
// 默认使用SimpleCookie
Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
cookie.setHttpOnly(true); //more secure, protects against XSS attacks
this.sessionIdCookie = cookie;
// 开启cookie
this.sessionIdCookieEnabled = true;
this.sessionIdUrlRewritingEnabled = false;
}
/**
* 保存session id cookie
*/
private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
if (currentId == null) {
String msg = "sessionId cannot be null when persisting for subsequent requests.";
throw new IllegalArgumentException(msg);
}
Cookie template = getSessionIdCookie();
Cookie cookie = new SimpleCookie(template);
String idString = currentId.toString();
cookie.setValue(idString);
cookie.saveTo(request, response);
log.trace("Set session ID cookie for session with id {}", idString);
}
/**
* 清除session id cookie
*/
private void removeSessionIdCookie(HttpServletRequest request, HttpServletResponse response) {
getSessionIdCookie().removeFrom(request, response);
}
/**
* 获取cookie中session id
*/
private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) {
if (!isSessionIdCookieEnabled()) {
log.debug("Session ID cookie is disabled - session id will not be acquired from a request cookie.");
return null;
}
if (!(request instanceof HttpServletRequest)) {
log.debug("Current request is not an HttpServletRequest - cannot get session ID cookie. Returning null.");
return null;
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
return getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));
}
/**
* 获取参考session id
*/
private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
// 获取cookie中session id
String id = getSessionIdCookieValue(request, response);
if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
} else {
//not in a cookie, or cookie is disabled - try the request URI as a fallback (i.e. due to URL rewriting):
//try the URI path segment parameters first:
id = getUriPathSegmentParamValue(request, ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
if (id == null) {
//not a URI path segment parameter, try the query parameters:
String name = getSessionIdName();
id = request.getParameter(name);
if (id == null) {
//try lowercase:
id = request.getParameter(name.toLowerCase());
}
}
if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
}
}
if (id != null) {
// 设置参考session id
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
//automatically mark it valid here. If it is invalid, the
//onUnknownSession method below will be invoked and we'll remove the attribute at that time.
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
}
// always set rewrite flag - SHIRO-361
request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, isSessionIdUrlRewritingEnabled());
return id;
}
/**
* 创建对外session
*/
//SHIRO-351
//also see http://cdivilly.wordpress.com/2011/04/22/java-servlets-uri-parameters/
//since 1.2.2
private String getUriPathSegmentParamValue(ServletRequest servletRequest, String paramName) {
if (!(servletRequest instanceof HttpServletRequest)) {
return null;
}
HttpServletRequest request = (HttpServletRequest)servletRequest;
String uri = request.getRequestURI();
if (uri == null) {
return null;
}
// 截取请求路径
int queryStartIndex = uri.indexOf('?');
if (queryStartIndex >= 0) { //get rid of the query string
uri = uri.substring(0, queryStartIndex);
}
// 检查分段标识
int index = uri.indexOf(';'); //now check for path segment parameters:
if (index < 0) {
//no path segment params - return:
return null;
}
//there are path segment params, let's get the last one that may exist:
final String TOKEN = paramName + "=";
// 截取段落参数
uri = uri.substring(index+1); //uri now contains only the path segment params
//we only care about the last JSESSIONID param:
index = uri.lastIndexOf(TOKEN);
if (index < 0) {
//no segment param:
return null;
}
uri = uri.substring(index + TOKEN.length());
index = uri.indexOf(';'); //strip off any remaining segment params:
if(index >= 0) {
uri = uri.substring(0, index);
}
return uri; //what remains is the value
}
/**
* 获取session id cookie名称
*/
//since 1.2.1
private String getSessionIdName() {
String name = this.sessionIdCookie != null ? this.sessionIdCookie.getName() : null;
if (name == null) {
// 默认值
name = ShiroHttpSession.DEFAULT_SESSION_ID_NAME;
}
return name;
}
/**
* 创建对外session
*/
protected Session createExposedSession(Session session, SessionContext context) {
if (!WebUtils.isWeb(context)) {
return super.createExposedSession(session, context);
}
ServletRequest request = WebUtils.getRequest(context);
ServletResponse response = WebUtils.getResponse(context);
SessionKey key = new WebSessionKey(session.getId(), request, response);
return new DelegatingSession(this, key);
}
/**
* 创建对外session
*/
protected Session createExposedSession(Session session, SessionKey key) {
if (!WebUtils.isWeb(key)) {
return super.createExposedSession(session, key);
}
ServletRequest request = WebUtils.getRequest(key);
ServletResponse response = WebUtils.getResponse(key);
SessionKey sessionKey = new WebSessionKey(session.getId(), request, response);
return new DelegatingSession(this, sessionKey);
}
/**
* 会话开始
*/
protected void onStart(Session session, SessionContext context) {
super.onStart(session, context);
if (!WebUtils.isHttp(context)) {
log.debug("SessionContext argument is not HTTP compatible or does not have an HTTP request/response " +
"pair. No session ID cookie will be set.");
return;
}
HttpServletRequest request = WebUtils.getHttpRequest(context);
HttpServletResponse response = WebUtils.getHttpResponse(context);
if (isSessionIdCookieEnabled()) {
Serializable sessionId = session.getId();
// 存储session id
storeSessionId(sessionId, request, response);
} else {
log.debug("Session ID cookie is disabled. No cookie has been set for new session with id {}", session.getId());
}
// 移除参考session id
request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
// 设置cookie更新标识
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
}
/**
* 获取session id
*/
@Override
public Serializable getSessionId(SessionKey key) {
Serializable id = super.getSessionId(key);
if (id == null && WebUtils.isWeb(key)) {
// 获取referenced session id
ServletRequest request = WebUtils.getRequest(key);
ServletResponse response = WebUtils.getResponse(key);
id = getSessionId(request, response);
}
return id;
}
/**
* 获取session id
*/
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
return getReferencedSessionId(request, response);
}
/**
* 会话过期
*/
@Override
protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) {
super.onExpiration(s, ese, key);
// 额外处理
onInvalidation(key);
}
/**
* 会话无效
*/
@Override
protected void onInvalidation(Session session, InvalidSessionException ise, SessionKey key) {
super.onInvalidation(session, ise, key);
// 额外处理
onInvalidation(key);
}
/**
* 会话无效
* 额外处理
*/
private void onInvalidation(SessionKey key) {
ServletRequest request = WebUtils.getRequest(key);
if (request != null) {
request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID);
}
if (WebUtils.isHttp(key)) {
log.debug("Referenced session was invalid. Removing session ID cookie.");
// 移除cookie
removeSessionIdCookie(WebUtils.getHttpRequest(key), WebUtils.getHttpResponse(key));
} else {
log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " +
"pair. Session ID cookie will not be removed due to invalidated session.");
}
}
/**
* 会话停止
*/
@Override
protected void onStop(Session session, SessionKey key) {
super.onStop(session, key);
if (WebUtils.isHttp(key)) {
HttpServletRequest request = WebUtils.getHttpRequest(key);
HttpServletResponse response = WebUtils.getHttpResponse(key);
log.debug("Session has been stopped (subject logout or explicit stop). Removing session ID cookie.");
// 移除cookie
removeSessionIdCookie(request, response);
} else {
log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " +
"pair. Session ID cookie will not be removed due to stopped session.");
}
}
/**
* 会话是否由底层Servlet容器管理
*/
public boolean isServletContainerSessions() {
// DefaultWebSessionManager是本地会话管理器
return false;
}
ServletContainerSessionManager
简介
Servlet容器的HttpSession
包装器,本身并不管理会话,因为Servlet容器提供了实际的管理支持;这个类的存在主要是为了“模拟”一个普通的Shiro SessionManager
,所以它可以插入到纯Web应用程序中的普通Shiro配置中;
由于此实现依赖于HttpSession
,因此它仅在Servlet容器中起作用,即除了使用HTTP协议的客户端之外,它无法支持任何客户端的会话;
核心方法
/**
* 根据上下文初始化数据创建新会话
*/
public Session start(SessionContext context) throws AuthorizationException {
return createSession(context);
}
/**
* 根据会话ID查找会话
*/
public Session getSession(SessionKey key) throws SessionException {
if (!WebUtils.isHttp(key)) {
String msg = "SessionKey must be an HTTP compatible implementation.";
throw new IllegalArgumentException(msg);
}
HttpServletRequest request = WebUtils.getHttpRequest(key);
Session session = null;
HttpSession httpSession = request.getSession(false);
if (httpSession != null) {
session = createSession(httpSession, request.getRemoteHost());
}
return session;
}
/**
* 获取指定会话创建时所在主机的hostname/ip
*/
private String getHost(SessionContext context) {
String host = context.getHost();
if (host == null) {
ServletRequest request = WebUtils.getRequest(context);
if (request != null) {
host = request.getRemoteHost();
}
}
return host;
}
/**
* 根据上下文初始化数据创建新会话
*/
protected Session createSession(SessionContext sessionContext) throws AuthorizationException {
if (!WebUtils.isHttp(sessionContext)) {
String msg = "SessionContext must be an HTTP compatible implementation.";
throw new IllegalArgumentException(msg);
}
HttpServletRequest request = WebUtils.getHttpRequest(sessionContext);
HttpSession httpSession = request.getSession();
//SHIRO-240: DO NOT use the 'globalSessionTimeout' value here on the acquired session.
//see: https://issues.apache.org/jira/browse/SHIRO-240
String host = getHost(sessionContext);
return createSession(httpSession, host);
}
/**
* 会话是否由底层Servlet容器管理
*/
protected Session createSession(HttpSession httpSession, String host) {
return new HttpServletSession(httpSession, host);
}
/**
* 会话是否由底层Servlet容器管理
*/
public boolean isServletContainerSessions() {
return true;
}