2021SC@SDUSC
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.ini.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.lang.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();
// 使用Session做一些事情(不需要web或EJB容器!!)
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
catch (AuthenticationException ae) {
}
}
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
currentUser.logout();
System.exit(0);
}
}
前面我们已经分析了“登录”功能的内部流程,而在获取Subject对象后,可以对session进行一些操作,接下来的几篇文章将会针对这段代码进行分析:
Session session = currentUser.getSession(); // 1
session.setAttribute("someKey", "aValue"); // 2
String value = (String) session.getAttribute("someKey"); // 3
if (value.equals("aValue")) { // 4
log.info("Retrieved the correct value! [" + value + "]");
}
Session是一个与单个Subject(用户、守护进程等)关联的有状态数据上下文,该Subject在一段时间内与软件系统进行交互。
会话的目的是由业务层管理,并通过其他层访问,而不需要绑定到任何给定的客户端的一种技术。这对Java系统来说是一个巨大的好处,因为到目前为止,唯一可行的会话机制是javax.servlet.http.HttpSession或有状态会话EJB,它们常常不必要地将应用程序与web或EJB技术耦合在一起。
本文主要介绍上述代码的1,即获取session的部分。
public class DelegatingSubject implements Subject {
// 对Subject接口的实现
// 返回与此主题关联的应用程序Session。如果调用此方法时不存在会话,则将创建一个与此Subject关联的新会话,然后返回。
public Session getSession() {
return getSession(true);
}
// 返回与此主题关联的应用程序Session。基于布尔参数,该方法的功能如下:
// 如果已经有一个与该Subject关联的现有会话,则返回该会话并忽略create参数。
// 如果没有会话存在,并且create为真,那么将创建一个新的会话,并与这个Subject关联,然后返回。
// 如果没有会话,并且create为false,则返回null。
public Session getSession(boolean create) {
if (log.isTraceEnabled()) {
log.trace("attempting to get session; create = " + create +
"; session is null = " + (this.session == null) +
"; session has id = " + (this.session != null && session.getId() != null));
}
if (this.session == null && create) {
//added in 1.2:
if (!isSessionCreationEnabled()) { // 1.1
String msg = "Session creation has been disabled for the current subject. This exception indicates " +
"that there is either a programming error (using a session when it should never be " +
"used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " +
"for the current Subject. See the " + DisabledSessionException.class.getName() + " JavaDoc " +
"for more.";
throw new DisabledSessionException(msg);
}
log.trace("Starting session for host {}", getHost());
// 创建session上下文
SessionContext sessionContext = createSessionContext(); // 1.2
Session session = this.securityManager.start(sessionContext); // 1.3
this.session = decorate(session); // 1.4
}
return this.session;
}
}
public class DelegatingSubject implements Subject {
protected boolean sessionCreationEnabled;
// 如果该Subject允许创建会话,则返回true,否则返回false。
protected boolean isSessionCreationEnabled() {
return this.sessionCreationEnabled;
}
}
public class DelegatingSubject implements Subject {
// SessionContext是呈现给SessionFactory的数据的“桶”,SessionFactory解释这些数据来构造会话实例。
// 它本质上是一个带有一些额外类型安全方法的数据Map,用于轻松检索通常用于构造Subject实例的对象。
// 虽然该接口包含用于公共数据类型的类型安全的setter和getter,但映射可以包含SessionFactory实现构造Session实例可能需要的任何额外内容。
// 用法:大多数Shiro终端用户不会直接使用SubjectContext实例,而是调用Subject.getSession()或Subject.getSession(boolean)方法
// 它们通常使用SessionContext实例与应用程序的SessionManager启动会话。
protected SessionContext createSessionContext() {
SessionContext sessionContext = new DefaultSessionContext();
if (StringUtils.hasText(host)) {
sessionContext.setHost(host);
}
return sessionContext;
}
}
// 抽象实现支持NativeSessionManager接口,支持SessionListeners和globalSessionTimeout的应用。
public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager, EventBusAware {
// 根据指定的上下文初始化数据启动一个新的会话,底层实现可以使用这些数据来确定如何准确地创建内部session实例。
// 此方法主要用于框架开发,因为实现通常会将参数传递给底层的SessionFactory,后者可以使用上下文以特定的方式构建内部Session实例。
// 这允许通过简单地将SessionFactory注入到SessionManager实例来插入Session创建逻辑。
public Session start(SessionContext context) {
Session session = createSession(context); // 1.3.1
applyGlobalSessionTimeout(session); // 1.3.2
onStart(session, context); // 1.3.3
notifyStart(session); // 1.3.4
// 不要将eis层的Session对象暴露给客户端层:
return createExposedSession(session, context); //1.3.5
}
}
EIS层包含了数据存储单元,通常的形式是数据库,不过它可以是提供数据的任何资源。它有可能是过时的遗留系统或文件系统。
EIS层通常指的是数据层、持久层与集成层。
public abstract class AbstractValidatingSessionManager extends AbstractNativeSessionManager
implements ValidatingSessionManager, Destroyable {
// 基于指定的(可能为空的)初始化数据创建一个新的Session Session实例。
// 实现类必须管理返回的会话的持久状态,以便以后可以通过getSession(SessionKey)方法获取它。
protected Session createSession(SessionContext context) throws AuthorizationException {
enableSessionValidationIfNecessary();
return doCreateSession(context);
}
}
public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager, EventBusAware {
protected void applyGlobalSessionTimeout(Session session) {
// 设置会话在到期前可能保持空闲的时间(以毫秒为单位)。
// 负值表示会话永远不会过期。
// 非负值(0或更大)表示如果会话空闲一段时间,会话将过期
// 如果你习惯了HttpSession的getMaxInactiveInterval()方法,这个方法的规模是不同的:
// Shiro会话使用毫秒值作为超时,而HttpSession.getMaxInactiveInterval使用秒。在Shiro会话中始终使用毫秒值。
session.setTimeout(getGlobalSessionTimeout());
onChange(session);
}
}
// 支持web应用程序的SessionManager实现。
public class DefaultWebSessionManager extends DefaultSessionManager implements WebSessionManager {
// 存储Session的ID,通常是一个Cookie,以与未来的请求相关联。
@Override
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();
storeSessionId(sessionId, request, response);
} else {
log.debug("Session ID cookie is disabled. No cookie has been set for new session with id {}", session.getId());
}
request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
}
}
public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager, EventBusAware {
// 通知任何感兴趣的SessionListeners会话已经启动。
// 这个方法在调用onStart方法之后调用。
protected void notifyStart(Session session) {
for (SessionListener listener : this.listeners) {
listener.onStart(session);
}
}
}
public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager, EventBusAware {
protected Session createExposedSession(Session session, SessionContext context) {
return new DelegatingSession(this, new DefaultSessionKey(session.getId()));
}
}
// DelegatingSession是服务器端会话的客户端层表示。这个实现基本上是服务器端NativeSessionManager的代理,它将为每个方法调用返回正确的结果。
// DelegatingSession将在适当的时候缓存数据以避免远程方法调用,只在必要的时候与服务器通信。
// 当然,如果在NativeSessionManager业务POJO的进程内使用,就像在web应用程序中,web类和服务器端业务POJO存在于同一个JVM中一样,则不会产生远程方法调用。
public class DelegatingSession implements Session, Serializable {
public DelegatingSession(NativeSessionManager sessionManager, SessionKey key) {
if (sessionManager == null) {
throw new IllegalArgumentException("sessionManager argument cannot be null.");
}
if (key == null) {
throw new IllegalArgumentException("sessionKey argument cannot be null.");
}
if (key.getSessionId() == null) {
String msg = "The " + DelegatingSession.class.getName() + " implementation requires that the " +
"SessionKey argument returns a non-null sessionId to support the " +
"Session.getId() invocations.";
throw new IllegalArgumentException(msg);
}
this.sessionManager = sessionManager;
this.key = key;
}
}
(完)
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 7swz.com 版权所有 赣ICP备2024042798号-8
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务