Spring Security解析八:SessionManagementConfigurer

在某些时候我们需要创建会话(HttpSession)来帮我们维持某些状态信息。例如当用户登录后在会话中记录用户的登录信息、或者存储一些用户数据等。Session本身是由Servlet容器进行管理,在内部可以完成Session的创建、销毁等, 当达到了会话的最大非活动间隔时长,那么会话会在服务器端会被失效。

Spring Security的会话管理并不是管理Session的创建销毁等工作,其主要用途大致有以下两点

  1. 单用户session的并发数限制与处理【一个用户多个地方登陆】
    处理方例如:
    1)超过限制跳转到指定的地址
    2)确定最近最少使用的会话,并将其标记为无效
  2. 激活会话固定保护机制,防止会话固定攻击

默认配置里面是包含对session进行管理的内容的,如下sessionManagement()所示

http
  .csrf().and()
  .addFilter(new WebAsyncManagerIntegrationFilter())
  .exceptionHandling().and()
  .headers().and()
  .sessionManagement().and()
  .securityContext().and()
  .requestCache().and()
  .anonymous().and()
  .servletApi().and()
  .apply(new DefaultLoginPageConfigurer<>()).and()
  .logout();

在调用sessionManagement()进行配置时,实际上是使用了SessionManagementConfigurer配置对象,如下方法所示

public SessionManagementConfigurer<HttpSecurity> sessionManagement() throws Exception {
    return getOrApply(new SessionManagementConfigurer<>());
}

SessionManagementConfigurer的源码如下

public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
        extends AbstractHttpConfigurer<SessionManagementConfigurer<H>, H> {
    //调用下面的方法得到ChangeSessionIdAuthenticationStrategy
    private final SessionAuthenticationStrategy DEFAULT_SESSION_FIXATION_STRATEGY = createDefaultSessionFixationProtectionStrategy();
    private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = this.DEFAULT_SESSION_FIXATION_STRATEGY;
    //会话认证(是否满足会话配置要求)策略
    private SessionAuthenticationStrategy sessionAuthenticationStrategy;
    private SessionAuthenticationStrategy providedSessionAuthenticationStrategy;
    //失效会话策略,如跳转到指定地址【SimpleRedirectInvalidSessionStrategy】
    private InvalidSessionStrategy invalidSessionStrategy;
    //用户会话信息过期策略【维护SessionInformation对象】
    private SessionInformationExpiredStrategy expiredSessionStrategy;
    //会话认证(是否满足会话配置要求)策略【下面的代码会陆续加入多个】
    private List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<>();
    //用户会话信息注册
    private SessionRegistry sessionRegistry;
    //最大会话数
    private Integer maximumSessions;
    //过期的Url
    private String expiredUrl;
    //是否达到最大会话数时阻止登录
    private boolean maxSessionsPreventsLogin;
    //会话创建策略
    private SessionCreationPolicy sessionPolicy;
    //是否启用会话Url重写
    private boolean enableSessionUrlRewriting;
    //无效的会话Url
    private String invalidSessionUrl;
    //会话身份验证错误URL
    private String sessionAuthenticationErrorUrl;
    private AuthenticationFailureHandler sessionAuthenticationFailureHandler;

    public SessionManagementConfigurer() {}

    private static SessionAuthenticationStrategy createDefaultSessionFixationProtectionStrategy() {
        // 默认使用HttpServletRequest.changeSessionId()防止会话固定攻击
        return new ChangeSessionIdAuthenticationStrategy();
    }

    //根据session的创建策略来判断,例如:总是创建【ALWAYS】、需要时创建【IF_REQUIRED】、
    //永不创建【STATELESS】 、永不创建但如有则使用【NEVER】
    private boolean isStateless() {
        SessionCreationPolicy sessionPolicy = getSessionCreationPolicy();
        return SessionCreationPolicy.STATELESS == sessionPolicy;
    }

    //是否允许创建session
    private boolean isAllowSessionCreation() {
        SessionCreationPolicy sessionPolicy = getSessionCreationPolicy();
        return SessionCreationPolicy.ALWAYS == sessionPolicy
                || SessionCreationPolicy.IF_REQUIRED == sessionPolicy;
    }

    SessionCreationPolicy getSessionCreationPolicy() {
        if (this.sessionPolicy != null) {
            return this.sessionPolicy;
        }
        SessionCreationPolicy sessionPolicy =
                getBuilder().getSharedObject(SessionCreationPolicy.class);
        return sessionPolicy == null ?
                SessionCreationPolicy.IF_REQUIRED : sessionPolicy;
    }


    @Override
    public void init(H http) {
        //获取存储SecurityContext的存储库
        //HttpSessionSecurityContextRepository、NullSecurityContextRepository
        SecurityContextRepository securityContextRepository = http
                .getSharedObject(SecurityContextRepository.class);
        //得到SessionCreationPolicy实例并判断策略(从属性或SharedObject中得到)
        boolean stateless = isStateless();

        if (securityContextRepository == null) {
            if (stateless) {
                //设置默认值
                http.setSharedObject(SecurityContextRepository.class,
                        new NullSecurityContextRepository());
            } else {
                HttpSessionSecurityContextRepository httpSecurityRepository = new HttpSessionSecurityContextRepository();
                //是否禁止URL的重写
                httpSecurityRepository
                        .setDisableUrlRewriting(!this.enableSessionUrlRewriting);
                //是否允许创建Session(当策略为ALWAYS或IF_REQUIRED时即可创建)
                httpSecurityRepository.setAllowSessionCreation(isAllowSessionCreation());
                //获取用于对身份令牌进行验证的解析类【anonymous、rememberMe】(AuthenticationTrustResolverImpl)
                AuthenticationTrustResolver trustResolver = http
                        .getSharedObject(AuthenticationTrustResolver.class);
                if (trustResolver != null) {
                    httpSecurityRepository.setTrustResolver(trustResolver);
                }
                http.setSharedObject(SecurityContextRepository.class,
                        httpSecurityRepository);
            }
        }
        //实现“保存请求”逻辑,请求被重定向到某身份认证机制返回后,允许该请求可以被取到并继续后续的请求。
        //(HttpSessionRequestCache、NullRequestCache)
        RequestCache requestCache = http.getSharedObject(RequestCache.class);
        if (requestCache == null) {
            if (stateless) {
                http.setSharedObject(RequestCache.class, new NullRequestCache());
            }
        }
        //这里需要关注,会使用到maximumSessions属性来控制会话数
        http.setSharedObject(SessionAuthenticationStrategy.class,
                getSessionAuthenticationStrategy(http));  //CompositeSessionAuthenticationStrategy

        //创建会话失效策略【执行重定向到固定URL】
        http.setSharedObject(InvalidSessionStrategy.class, getInvalidSessionStrategy());
    }

    @Override
    public void configure(H http) {
        SecurityContextRepository securityContextRepository = http
                .getSharedObject(SecurityContextRepository.class);
        //创建用于会话管理的过滤器【关键】
        SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(
                securityContextRepository, getSessionAuthenticationStrategy(http));
        if (this.sessionAuthenticationErrorUrl != null) {
            //会话验证错误跳转地址
            sessionManagementFilter.setAuthenticationFailureHandler(
                    new SimpleUrlAuthenticationFailureHandler(
                            this.sessionAuthenticationErrorUrl));
        }
        /**
        *会话失效管理策略;
        *
        *SimpleRedirectInvalidSessionStrategy:当检测到无效的请求会话时,
        *执行重定向到固定URL
        */
        InvalidSessionStrategy strategy = getInvalidSessionStrategy();
        if (strategy != null) {
            sessionManagementFilter.setInvalidSessionStrategy(strategy);
        }
        //会话验证失败处理器
        AuthenticationFailureHandler failureHandler = getSessionAuthenticationFailureHandler();
        if (failureHandler != null) {
            sessionManagementFilter.setAuthenticationFailureHandler(failureHandler);
        }
        //身份验证令牌验证
        AuthenticationTrustResolver trustResolver = http
                .getSharedObject(AuthenticationTrustResolver.class);
        if (trustResolver != null) {
            sessionManagementFilter.setTrustResolver(trustResolver);
        }
        sessionManagementFilter = postProcess(sessionManagementFilter);
        //往HttpSecurity实例中添加会话管理的Filter
        http.addFilter(sessionManagementFilter);
        if (isConcurrentSessionControlEnabled()) {   //如果maximumSessions属性不为空
            //对Session的并发量(同用户打开的session数)进行控制
            //【如果maximumSessions属性不为空】
            ConcurrentSessionFilter concurrentSessionFilter = createConcurrencyFilter(http);

            concurrentSessionFilter = postProcess(concurrentSessionFilter);
            //往HttpSecurity实例中添加Filter
            http.addFilter(concurrentSessionFilter);
        }
    }

    InvalidSessionStrategy getInvalidSessionStrategy() {
        if (this.invalidSessionStrategy != null) {  //默认为空
            return this.invalidSessionStrategy;
        }
        if (this.invalidSessionUrl != null) {  //默认为空
            //创建会话失效策略【执行重定向到固定URL】
            this.invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(
                    this.invalidSessionUrl);
        }
        if (this.invalidSessionUrl == null) {
            return null;
        }
        if (this.invalidSessionStrategy == null) {
            this.invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(
                    this.invalidSessionUrl);
        }
        return this.invalidSessionStrategy;
    }

    //得到会话验证策略【CompositeSessionAuthenticationStrategy】
    private SessionAuthenticationStrategy getSessionAuthenticationStrategy(H http) {
        if (this.sessionAuthenticationStrategy != null) { //默认为空
            return this.sessionAuthenticationStrategy;
        }
        //集合实例
        List<SessionAuthenticationStrategy> delegateStrategies = this.sessionAuthenticationStrategies;
        //创建默认的会话认证测试,默认使用sessionFixationAuthenticationStrategy【ChangeSessionIdAuthenticationStrategy】
        SessionAuthenticationStrategy defaultSessionAuthenticationStrategy;
        if (this.providedSessionAuthenticationStrategy == null) {   //默认为空
            // If the user did not provide a SessionAuthenticationStrategy
            // then default to sessionFixationAuthenticationStrategy
            defaultSessionAuthenticationStrategy = postProcess(
                    this.sessionFixationAuthenticationStrategy);
        }
        else {
            defaultSessionAuthenticationStrategy = this.providedSessionAuthenticationStrategy;
        }
        //是否允许并发会话控制(通过判断maximumSessions属性是否为空,默认为空-即不控制)
        if (isConcurrentSessionControlEnabled()) {
            //默认使用SessionRegistryImpl,监听ApplicationContext的SessionDestroyedEvent事件
            SessionRegistry sessionRegistry = getSessionRegistry(http);

            //使用sessionRegistry构建ConcurrentSessionControlAuthenticationStrategy实例
            //内部会调用sessionRegistry的getAllSessions方法得到登录用户的所有session,然后判断个数是否超出限制,
            ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlAuthenticationStrategy(
                    sessionRegistry);
            //设置限制的并发会话数
            concurrentSessionControlStrategy.setMaximumSessions(this.maximumSessions);
            //是否阻止用户打开超出允许的会话。
            //如果设置为true,将引发SessionAuthenticationException,这意味着将阻止用户身份验证。
            //如果设置为false,则将确定最近最少使用的会话,并将其标记为无效。默认false。
            concurrentSessionControlStrategy
                    .setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin);
            concurrentSessionControlStrategy = postProcess(
                    concurrentSessionControlStrategy);

            //使用sessionRegistry创建RegisterSessionAuthenticationStrategy实例。
            //用于在成功验证后向SessionRegistry注册用户的策略(执行sessionRegistry的registerNewSession方法)
            RegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy(
                    sessionRegistry);
            registerSessionStrategy = postProcess(registerSessionStrategy);

            //将上面创建的三个策略添加到集合中
            delegateStrategies.addAll(Arrays.asList(concurrentSessionControlStrategy,
                    defaultSessionAuthenticationStrategy, registerSessionStrategy));
        }
        else {
            //值添加默认的策略
            delegateStrategies.add(defaultSessionAuthenticationStrategy);
        }
        //生成复合会话认证策略,方便同时使用多个策略
        this.sessionAuthenticationStrategy = postProcess(
                new CompositeSessionAuthenticationStrategy(delegateStrategies));
        return this.sessionAuthenticationStrategy;
    }

    private ConcurrentSessionFilter createConcurrencyFilter(H http) {
        /*
        *session的失效策略,如:
        *1,SimpleRedirectSessionInformationExpiredStrategy  检测到过期会话时,执行重定向到固定URL
        */
        SessionInformationExpiredStrategy expireStrategy = getExpiredSessionStrategy();
        SessionRegistry sessionRegistry = getSessionRegistry(http);
        ConcurrentSessionFilter concurrentSessionFilter;
        if (expireStrategy == null) {
            concurrentSessionFilter = new ConcurrentSessionFilter(sessionRegistry);
        } else {
            concurrentSessionFilter = new ConcurrentSessionFilter(sessionRegistry, expireStrategy);
        }
        LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
        if (logoutConfigurer != null) {
            List<LogoutHandler> logoutHandlers = logoutConfigurer.getLogoutHandlers();
            if (!CollectionUtils.isEmpty(logoutHandlers)) {
                concurrentSessionFilter.setLogoutHandlers(logoutHandlers);
            }
        }
        return concurrentSessionFilter;
    }

    //维护SessionInformation实例的注册表
    private SessionRegistry getSessionRegistry(H http) {
        if (this.sessionRegistry == null) {
            SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
            registerDelegateApplicationListener(http, sessionRegistry);
            this.sessionRegistry = sessionRegistry;
        }
        return this.sessionRegistry;
    }

    private void registerDelegateApplicationListener(H http,
            ApplicationListener<?> delegate) {
        ApplicationContext context = http.getSharedObject(ApplicationContext.class);
        if (context == null) {
            return;
        }
        if (context.getBeansOfType(DelegatingApplicationListener.class).isEmpty()) {
            return;
        }
        DelegatingApplicationListener delegating = context
                .getBean(DelegatingApplicationListener.class);
        SmartApplicationListener smartListener = new GenericApplicationListenerAdapter(
                delegate);
        delegating.addListener(smartListener);
    }
}

SessionRegistryImpl

/**
 * org.springframework.security.core.session.SessionRegistry的默认实现,
 *它侦听在Spring应用程序上下文中发布的
 * org.springframework.security.core.session.SessionDestroyedEvent事件。
 * 
 * 要使此类在web应用程序中正常工作,必须在web.xml文件中注册HttpSessionEventPublisher,以便通知此类会话已过期。
 */
public class SessionRegistryImpl implements SessionRegistry,
        ApplicationListener<SessionDestroyedEvent> {

    // ~ Instance fields
    // ================================================================================================

    protected final Log logger = LogFactory.getLog(SessionRegistryImpl.class);

    /** <principal:Object,SessionIdSet> */
    private final ConcurrentMap<Object, Set<String>> principals;
    /** <sessionId:Object,SessionInformation> */
    private final Map<String, SessionInformation> sessionIds;

    // ~ Methods
    // ========================================================================================================

    public SessionRegistryImpl() {
        this.principals = new ConcurrentHashMap<>();
        this.sessionIds = new ConcurrentHashMap<>();
    }

    public SessionRegistryImpl(ConcurrentMap<Object, Set<String>> principals, Map<String, SessionInformation> sessionIds) {
        this.principals=principals;
        this.sessionIds=sessionIds;
    }

    public List<Object> getAllPrincipals() {
        return new ArrayList<>(principals.keySet());
    }

    public List<SessionInformation> getAllSessions(Object principal,
            boolean includeExpiredSessions) {
        final Set<String> sessionsUsedByPrincipal = principals.get(principal);

        if (sessionsUsedByPrincipal == null) {
            return Collections.emptyList();
        }

        List<SessionInformation> list = new ArrayList<>(
                sessionsUsedByPrincipal.size());

        for (String sessionId : sessionsUsedByPrincipal) {
            SessionInformation sessionInformation = getSessionInformation(sessionId);

            if (sessionInformation == null) {
                continue;
            }

            if (includeExpiredSessions || !sessionInformation.isExpired()) {
                list.add(sessionInformation);
            }
        }

        return list;
    }

    public SessionInformation getSessionInformation(String sessionId) {
        Assert.hasText(sessionId, "SessionId required as per interface contract");

        return sessionIds.get(sessionId);
    }

    //通知SessionDestroyedEvent 事件。
    //问题:具体派发该事件的地方在哪里?
    public void onApplicationEvent(SessionDestroyedEvent event) {
        //过期session的ID
        String sessionId = event.getId();
        //移除该session的信息
        removeSessionInformation(sessionId);
    }

    //更新最后的访问时间
    public void refreshLastRequest(String sessionId) {
        Assert.hasText(sessionId, "SessionId required as per interface contract");

        SessionInformation info = getSessionInformation(sessionId);

        if (info != null) {
            info.refreshLastRequest();
        }
    }

    public void registerNewSession(String sessionId, Object principal) {
        Assert.hasText(sessionId, "SessionId required as per interface contract");
        Assert.notNull(principal, "Principal required as per interface contract");

        if (getSessionInformation(sessionId) != null) {
            removeSessionInformation(sessionId);
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Registering session " + sessionId + ", for principal "
                    + principal);
        }

        sessionIds.put(sessionId,
                new SessionInformation(principal, sessionId, new Date()));

        principals.compute(principal, (key, sessionsUsedByPrincipal) -> {
            if (sessionsUsedByPrincipal == null) {
                sessionsUsedByPrincipal = new CopyOnWriteArraySet<>();
            }
            sessionsUsedByPrincipal.add(sessionId);

            if (logger.isTraceEnabled()) {
                logger.trace("Sessions used by '" + principal + "' : "
                        + sessionsUsedByPrincipal);
            }
            return sessionsUsedByPrincipal;
        });
    }

    public void removeSessionInformation(String sessionId) {
        Assert.hasText(sessionId, "SessionId required as per interface contract");

        SessionInformation info = getSessionInformation(sessionId);

        if (info == null) {
            return;
        }

        if (logger.isTraceEnabled()) {
            logger.debug("Removing session " + sessionId
                    + " from set of registered sessions");
        }

        sessionIds.remove(sessionId);

        principals.computeIfPresent(info.getPrincipal(), (key, sessionsUsedByPrincipal) -> {
            if (logger.isDebugEnabled()) {
                logger.debug("Removing session " + sessionId
                        + " from principal's set of registered sessions");
            }

            sessionsUsedByPrincipal.remove(sessionId);

            if (sessionsUsedByPrincipal.isEmpty()) {
                // No need to keep object in principals Map anymore
                if (logger.isDebugEnabled()) {
                    logger.debug("Removing principal " + info.getPrincipal()
                            + " from registry");
                }
                sessionsUsedByPrincipal = null;
            }

            if (logger.isTraceEnabled()) {
                logger.trace("Sessions used by '" + info.getPrincipal() + "' : "
                        + sessionsUsedByPrincipal);
            }
            return sessionsUsedByPrincipal;
        });
    }

}

SessionInformation

/**
 * Represents a record of a session within the Spring Security framework.
 * <p>
 * This is primarily used for concurrent session support.
 * <p>
 * Sessions have three states: active, expired, and destroyed. A session can that is
 * invalidated by <code>session.invalidate()</code> or via Servlet Container management is
 * considered "destroyed". An "expired" session, on the other hand, is a session that
 * Spring Security wants to end because it was selected for removal for some reason
 * (generally as it was the least recently used session and the maximum sessions for the
 * user were reached). An "expired" session is removed as soon as possible by a
 * <code>Filter</code>.
 *
 * @author Ben Alex
 */
public class SessionInformation implements Serializable {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    // ~ Instance fields
    // ================================================================================================

    private Date lastRequest;
    private final Object principal;
    private final String sessionId;
    private boolean expired = false;

    // ~ Constructors
    // ===================================================================================================

    public SessionInformation(Object principal, String sessionId, Date lastRequest) {
        Assert.notNull(principal, "Principal required");
        Assert.hasText(sessionId, "SessionId required");
        Assert.notNull(lastRequest, "LastRequest required");
        this.principal = principal;
        this.sessionId = sessionId;
        this.lastRequest = lastRequest;
    }

    // ~ Methods
    // ========================================================================================================

    public void expireNow() {
        this.expired = true;
    }

    public Date getLastRequest() {
        return lastRequest;
    }

    public Object getPrincipal() {
        return principal;
    }

    public String getSessionId() {
        return sessionId;
    }

    public boolean isExpired() {
        return expired;
    }

    /**
     * Refreshes the internal lastRequest to the current date and time.
     */
    public void refreshLastRequest() {
        this.lastRequest = new Date();
    }
}

以上的都是初始化和装配的过程,下面再看看对请求进行拦截时候的执行过程

1,SessionManagementFilter

/**
*检测到自请求开始以来用户已通过身份验证,如果用户已通过身份验证,
*则调用配置的SessionAuthenticationStrategy执行任何与会话相关的活动,
*例如激活会话固定保护机制或检查多个并发登录。
*/
public class SessionManagementFilter extends GenericFilterBean {
    // ~ Static fields/initializers
    // =====================================================================================

    static final String FILTER_APPLIED = "__spring_security_session_mgmt_filter_applied";

    // ~ Instance fields
    // ================================================================================================

    private final SecurityContextRepository securityContextRepository;
    private SessionAuthenticationStrategy sessionAuthenticationStrategy;
    private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
    private InvalidSessionStrategy invalidSessionStrategy = null;
    private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();

    public SessionManagementFilter(SecurityContextRepository securityContextRepository) {
        this(securityContextRepository, new SessionFixationProtectionStrategy());
    }

    public SessionManagementFilter(SecurityContextRepository securityContextRepository,
            SessionAuthenticationStrategy sessionStrategy) {
        Assert.notNull(securityContextRepository,
                "SecurityContextRepository cannot be null");
        Assert.notNull(sessionStrategy, "SessionAuthenticationStrategy cannot be null");
        this.securityContextRepository = securityContextRepository;
        this.sessionAuthenticationStrategy = sessionStrategy;
    }

    //开始执行过滤器
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        if (request.getAttribute(FILTER_APPLIED) != null) {
            chain.doFilter(request, response);
            return;
        }

        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

        //HttpSessionSecurityContextRepository
        //NullSecurityContextRepository
        if (!securityContextRepository.containsContext(request)) {
            //注意:这里可是在使用Authentication哦!!!
            Authentication authentication = SecurityContextHolder.getContext()
                    .getAuthentication();

            if (authentication != null && !trustResolver.isAnonymous(authentication)) {
                // 用户在当前请求期间已通过身份验证
                try {
                    //典型的用法是确保会话存在,或者更改会话Id以防止会话固定攻击。
                    //那么这里用的是哪个实现类呢?
                    sessionAuthenticationStrategy.onAuthentication(authentication,
                            request, response);
                }
                catch (SessionAuthenticationException e) {
                    // The session strategy can reject the authentication
                    logger.debug(
                            "SessionAuthenticationStrategy rejected the authentication object",
                            e);
                    SecurityContextHolder.clearContext();
                    failureHandler.onAuthenticationFailure(request, response, e);

                    return;
                }
                // Eagerly save the security context to make it available for any possible
                // re-entrant
                // requests which may occur before the current request completes.
                // SEC-1396.
                securityContextRepository.saveContext(SecurityContextHolder.getContext(),
                        request, response);
            }
            else {
                // No security context or authentication present. Check for a session
                // timeout
                if (request.getRequestedSessionId() != null
                        && !request.isRequestedSessionIdValid()) {  //如果请求的会话已经无效
                    if (logger.isDebugEnabled()) {
                        logger.debug("Requested session ID "
                                + request.getRequestedSessionId() + " is invalid.");
                    }

                    if (invalidSessionStrategy != null) {
                        //对无效的会话进行处理
                        invalidSessionStrategy
                                .onInvalidSessionDetected(request, response);
                        return;
                    }
                }
            }
        }

        chain.doFilter(request, response);
    }
}

2,ConcurrentSessionFilter

如果设置了才会执行该Filter

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        HttpSession session = request.getSession(false);

        if (session != null) {
            SessionInformation info = sessionRegistry.getSessionInformation(session
                    .getId());

            if (info != null) {
                if (info.isExpired()) {
                    // Expired - abort processing
                    if (logger.isDebugEnabled()) {
                        logger.debug("Requested session ID "
                                + request.getRequestedSessionId() + " has expired.");
                    }
                    doLogout(request, response);

                    this.sessionInformationExpiredStrategy.onExpiredSessionDetected(new SessionInformationExpiredEvent(info, request, response));
                    return;
                }
                else {
                    // Non-expired - update last request date/time
                    sessionRegistry.refreshLastRequest(info.getSessionId());
                }
            }
        }

        chain.doFilter(request, response);
    }

下面对上面场景的几个类型进行简单的梳理:

1,SessionAuthenticationStrategy

会话认证策略,默认为会话固定策略

  • ChangeSessionIdAuthenticationStrategy 【默认】
  • SessionFixationProtectionStrategy
  • CompositeSessionAuthenticationStrategy
  • ConcurrentSessionControlAuthenticationStrategy
  • CsrfAuthenticationStrategy
  • NullAuthenticatedSessionStrategy
  • RegisterSessionAuthenticationStrategy
2,InvalidSessionStrategy

无效(失效)会话策略;

  • SimpleRedirectInvalidSessionStrategy
3,SessionInformationExpiredStrategy

会话信息过期策略

  • SimpleRedirectSessionInformationExpiredStrategy
  • ResponseBodySessionInformationExpiredStrategy【ConcurrentSessionFilter的内部类】
4,SessionRegistry

会话注册表

  • SessionRegistryImpl
5,SessionCreationPolicy【枚举】

会话创建策略.

  • ALWAYS:始终创建HttpSession
  • NEVER:从不创建,使用存在的HttpSession;
  • IF_REQUIRED: 需要时创建HttpSession
  • STATELESS:从不创建也不使用HttpSession
6,AuthenticationFailureHandler

认证失败处理器

  • AuthenticationEntryPointFailureHandler
  • DelegatingAuthenticationFailureHandler
  • ForwardAuthenticationFailureHandler
  • SimpleUrlAuthenticationFailureHandler
  • ExceptionMappingAuthenticationFailureHandler
7,AuthenticationTrustResolver

对Token进行验证,主要针对:Anonymous、RememberMe

  • AuthenticationTrustResolverImpl
8,SecurityContextRepository
  • HttpSessionSecurityContextRepository
  • NullSecurityContextRepository
9,RequestCache
  • HttpSessionRequestCache
  • NullRequestCache
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容