您好,欢迎来到微智科技网。
搜索
您的当前位置:首页Shiro源码分析(八)——获取Session

Shiro源码分析(八)——获取Session

来源:微智科技网

2021SC@SDUSC

一.Demo代码

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的部分。

1

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;
    }
}

1.1

public class DelegatingSubject implements Subject {
	protected boolean sessionCreationEnabled;
	// 如果该Subject允许创建会话,则返回true,否则返回false。
    protected boolean isSessionCreationEnabled() {
        return this.sessionCreationEnabled;
    }
}

1.2

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;
    }
}

1.3

// 抽象实现支持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层通常指的是数据层、持久层与集成层。

1.3.1

public abstract class AbstractValidatingSessionManager extends AbstractNativeSessionManager
        implements ValidatingSessionManager, Destroyable {
    // 基于指定的(可能为空的)初始化数据创建一个新的Session Session实例。
    // 实现类必须管理返回的会话的持久状态,以便以后可以通过getSession(SessionKey)方法获取它。
    protected Session createSession(SessionContext context) throws AuthorizationException {
        enableSessionValidationIfNecessary();
        return doCreateSession(context);
    }
}

1.3.2

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);
    }
}

1.3.3

// 支持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);
    }
}

1.3.4

public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager, EventBusAware {
	// 通知任何感兴趣的SessionListeners会话已经启动。
	// 这个方法在调用onStart方法之后调用。
    protected void notifyStart(Session session) {
        for (SessionListener listener : this.listeners) {
            listener.onStart(session);
        }
    }
}

1.3.5

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

本站由北京市万商天勤律师事务所王兴未律师提供法律服务