【问题标题】:How to set absolute session timeout for a Spring Session如何为 Spring Session 设置绝对会话超时
【发布时间】:2020-07-18 02:27:12
【问题描述】:

根据OWASP 会话必须有一个绝对超时,它定义了会话可以处于活动状态的最长时间。我知道如何使用server.servlet.session.timeout 设置春季会话的最大不活动超时,但是我不确定如何设置会话的绝对超时。我想我可以为 Cookie 设置 Max-Age 属性,这可能会用作绝对超时,但是我想知道是否可以在服务器端会话上以某种方式设置绝对超时?

【问题讨论】:

    标签: spring session session-cookies spring-session


    【解决方案1】:

    此功能未在 Spring 会话中实现。 有关解决方法,请参阅 https://github.com/spring-projects/spring-session/issues/922

    【讨论】:

    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
    • 嗨@manofaction,请用 borchvm 提到的基本部分更新答案,我会接受。
    • 我自己实际上还没有尝试过解决方案。实际上我也在类似的基础上提出了一个问题stackoverflow.com/questions/63830026/…不是重复的而是相似的,因为我希望不基于用户交互来删除会话。
    【解决方案2】:

    Spring Sessions 没有内置的方法可以做到这一点。但是,有一些解决方法。

    一种解决方案是围绕存储库创建一个 cglib 代理。 这是一个基于 JdbcIndexedSessionRepository 的示例,但它应该是相对的,可以将其扩展到任何其他会话存储库。

    需要注意的是,如果 SessionRepository 超类没有默认构造函数,cglib 要求您为构造函数传入参数。在 JdbcIndexedSessionRepository 的情况下,参数需要不为空,但它们是接口,Spring 提供了易于构造的实现。我本可以使用反射从现有存储库中获取对象,但没有必要这样做,因为从未实际使用过代理的超类。

    @Configuration
    class SessionConfiguration {
    
        public static final Duration ABSOLUTE_SESSION_TIMEOUT = Duration.of(8, ChronoUnit.HOURS);
    
        /**
         * Configures the session repository to have all sessions timeout after at least
         * {@link SessionConfiguration#ABSOLUTE_SESSION_TIMEOUT}.
         *
         * Requires the session repository to be a JdbcIndexedSessionRepository.
         */
        @Bean
        public static BeanPostProcessor absoluteSessionRepositoryBeanPostProcessor() {
            return new BeanPostProcessor() {
                @Override
                public Object postProcessAfterInitialization(@Nonnull Object bean, @Nonnull String beanName) {
                    if (!(bean instanceof JdbcIndexedSessionRepository)) {
                        return bean;
                    }
    
                    Enhancer enhancer = new Enhancer();
                    enhancer.setSuperclass(JdbcIndexedSessionRepository.class);
                    enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
                        if (!method.getName().equals("findById")) {
                            return method.invoke(bean, args);
                        }
    
                        Session session = (Session) method.invoke(bean, args);
                        if (session == null) {
                            return null;
                        }
    
                        Instant timeoutTime = session.getCreationTime().plus(ABSOLUTE_SESSION_TIMEOUT);
                        if (timeoutTime.compareTo(Instant.now()) < 0) {
                            return null;
                        }
    
                        return session;
                    });
    
                    return enhancer.create(
                            new Class[]{JdbcOperations.class, TransactionOperations.class},
                            new Object[]{new JdbcTemplate(), new TransactionTemplate()}
                    );
                }
            };
        }
    
    }
    

    另一个解决方案是使用 SessionRepositoryCustomizer 来更改存储库调用的 SQL 查询。当然,这仅适用于您的存储库基于 SQL 数据库,例如 JdbcIndexedSessionRepository。 此代码使用 MySQL 特定行为UNIX_TIMESTAMP() 函数,但将其扩展到大多数其他 SQL 数据库应该相对简单。

    @Configuration
    class SessionConfiguration {
    
        public static final Duration ABSOLUTE_SESSION_TIMEOUT = Duration.of(8, ChronoUnit.HOURS);
    
        /**
         * Configures the session repository to have all sessions timeout after
         * {@link SessionConfiguration#ABSOLUTE_SESSION_TIMEOUT}.
         *
         * Requires the session repository to be a JdbcIndexedSessionRepository and for the database to be MySQL.
         * The customizer sets a custom query that relies on the MySQL-specific UNIX_TIMESTAMP function.
         *
         * UNIX_TIMESTAMP() returns the current time in seconds since the epoch while the session's CREATION_TIME is in
         * milliseconds since the epoch, so we need to multiply the current time by 1000 to get comparable numbers.
         * See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp for more
         */
        @Bean
        public SessionRepositoryCustomizer<JdbcIndexedSessionRepository> absoluteTimeoutSessionRepositoryCustomizer() {
            return sessionRepository -> {
                String query = "SELECT S.PRIMARY_ID, S.SESSION_ID, S.CREATION_TIME, S.LAST_ACCESS_TIME, "
                        + "S.MAX_INACTIVE_INTERVAL, SA.ATTRIBUTE_NAME, SA.ATTRIBUTE_BYTES "
                        + "FROM %TABLE_NAME% S "
                        + "LEFT JOIN %TABLE_NAME%_ATTRIBUTES SA ON S.PRIMARY_ID = SA.SESSION_PRIMARY_ID "
                        + "WHERE S.SESSION_ID = ? AND (UNIX_TIMESTAMP() * 1000 - S.CREATION_TIME) < "
                        + ABSOLUTE_SESSION_TIMEOUT.toMillis();
                sessionRepository.setGetSessionQuery(query);
            };
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-28
      • 2016-08-16
      • 2017-04-19
      • 2012-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多