Documentation Home

Multi-Customer Assisted Shopping

Broadleaf's CSR feature, allows for simultaneous multi-session customer assisted shopping.

How it works

This feature leverages and builds on top of Spring Session to handle "multi-session" management.
Spring Session requires a "backing repository" in order to store all attributes across simultaneous sessions.
By default, Broadleaf provides a JDBC backing repository out-of-box that extends Spring Session's JDBC Repo
(http://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-jdbc). However, you are free
to switch out this backing repo with anything Spring Session supports, for example: Redis, Mongo, HazelCast, etc...

The mechanics behind this involve using a single session to maintain multiple "session aliases".
Internally Broadleaf uses Spring Session's CookieHttpSessionStrategy
to maintain session aliases between requests. Since a single session may contain many session aliases,
every request must have a session identifier in order to maintain context between requests.
Broadleaf provides a Javascript library that will intercept all anchor, form, and ajax requests and append the appropriate alias based on the
pages current context. In order to maintain internally initiated redirects and forwards, Broadleaf also
provides a BroadleafThymeleafViewResolver override to amend the request with any session alias context.

Requirements

By default, this feature is disabled as there are a few requirements that your application must meet in order
for "multi-session" flows to function properly. It is also recommended that a thorough test be performed on the overall site
after being enabled to ensure that correct alias context is maintained throughout a shopping flow (i.e. no JavaScript/XHR calls outside
the context of normal DOM anchors or JQuery's Ajax calls drop the current session alias).

All URLs in your site application must be served under a single protocol, in most cases this would be HTTPS. This is due to the way cookies (auth and session fixation protection) are set and invalidated across multiple sessions. You can configure all your intercept-url's to use "HTTPS" by changing your configuration in applicationContext-security.xml

...
<sec:intercept-url pattern="/" requires-channel="https"/>
<sec:intercept-url pattern="/**" requires-channel="https"/>
...

Or you can configure all your intercept-url's to use "HTTPS" by changing your java configuration in your WebSecurityConfigurerAdapter configuration class

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
    ...
        .requiresChannel()
            .antMatchers("/","/**")
            .requiresSecure()
            .and()
    ...
}

By default, multi-session is only enabled for CSR Assisted Shopping "front-office" requests. Any "back-office" CSR site functions (such as Change Order) will not work in conjunction with any already provisioned csr shopping sessions. Any request initiated from a "back-office" request will invalidate any already initiated "front-office" request and vice-versa.

Broadleaf, by default, doesn't use Spring Security's built-in session fixation protection. Broadleaf has a custom security filter "blSessionFixationProtectionFilter" that manages an encrypted secure "http only" cookie between both secure and non-secure requests to preserve session ids. This filter must be adapted to handle multi-sessions as well. A new filter must be specified - which is outlined in the installation steps below.

Installation

  1. The Spring Session dependency is not included by default. Include it

If you are using a JDBC backed repo to store the session attributes (the default) you must include the following
dependency in your root pom.xml

xml
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
<!-- The version is usually inherited from the boot parent -->
</dependency>

If you aren't using a JDBC backed session repo then you'll need the following

xml
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
<!-- The version is usually inherited from the boot parent -->
</dependency>

  1. If you are using a JDBC/Database backed repo, you must make the following schema changes:

    Note: the following sample SQL below uses HSQLDB syntax, if you are using another DB, please use one of the Spring Session provided schema scripts located here as a starting point: https://github.com/spring-projects/spring-session/tree/2.0.4.RELEASE/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc

    DROP TABLE BLC_SPRING_SESSION_ATTRIBUTES IF EXISTS;
    DROP TABLE BLC_SPRING_SESSION IF EXISTS;
    
    CREATE TABLE BLC_SPRING_SESSION (PRIMARY_ID CHAR(36) NOT NULL, SESSION_ID CHAR(36) NOT NULL, CREATION_TIME BIGINT NOT NULL, LAST_ACCESS_TIME BIGINT NOT NULL, MAX_INACTIVE_INTERVAL INT NOT NULL, EXPIRY_TIME BIGINT NOT NULL, PRINCIPAL_NAME VARCHAR(100), CONSTRAINT BLC_SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID));
    
    CREATE UNIQUE INDEX BLC_SPRING_SESSION_IX1 ON BLC_SPRING_SESSION (SESSION_ID);
    CREATE INDEX BLC_SPRING_SESSION_IX2 ON BLC_SPRING_SESSION (EXPIRY_TIME);
    CREATE INDEX BLC_SPRING_SESSION_IX3 ON BLC_SPRING_SESSION (PRINCIPAL_NAME);
    
    CREATE TABLE BLC_SPRING_SESSION_ATTRIBUTES (SESSION_PRIMARY_ID CHAR(36) NOT NULL, ATTRIBUTE_NAME VARCHAR(200) NOT NULL, ATTRIBUTE_BYTES LONGVARBINARY NOT NULL, CONSTRAINT BLC_SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME), CONSTRAINT BLC_SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES BLC_SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE);
    

    Notice that to keep convention with the framework, we've prefixed the "oob spring" table names with BLC_ and you should do the same.

  2. To enable multi-session assisted shopping in your application, you must set the following system property in common-shared.propterties:

    csr.multi.customer.session.enabled=true
    

    If for some reason, your application is all served under HTTP (e.g. just in dev), you must set the following
    system property:

    csr.multi.customer.session.https=false
    
  3. Place the filter beans into the security filter chain:

    Add blCSRMultiSessionInitFilter as the very first filter in your blPreSecurityFilterChain

    Add blCSRMultiSessionCleanupFilter as the very last filter in your blPostSecurityFilterChain

    If you are using spring boot, then you do not need specify filters in a filter chain, Spring Boot will register them by default.

  4. In addition, in order to validate multiple active CSR requests in the above filters, the CrossAppAuthCSRMultiSessionServiceImpl is configured to generate a unique "token" for each active CSR session (i.e. this token is passed with each request via a cookie, "BLC_CSR_MULTI_AUTH_n"). This service looks at the system property csr.multi.customer.session.secretKey to generate this token. This property should be set in PRODUCTION to an appropriate secret key. By default, it is configured to use the value: "blc-default-key" if nothing is specified. This service uses "HmacSHA1" in conjunction with the secret key to create the appropriate token, which is then verified in the above blCSRMultiSessionInitFilter.

  5. Make sure all intercept-urls require either all HTTPS or all HTTP see requirements section above for details.

  6. Setup the session fixation protection filter:

    XML Configuration:

    In site/src/main/webapp/WEB-INF/applicationContext-security.xml:

    <sec:custom-filter ref="blMultiSessionFixationProtectionFilter" before="SESSION_MANAGEMENT_FILTER"/>
    

    Java Configuration:

    In the security configuration class, add a filter registration bean to prevent the filter from being automatically registered and instead register it manually before the session management filter:

    @Bean
    public FilterRegistrationBean blMultiSessionFixationProtectionFilterFilterRegistrationBean(@Qualifier("blMultiSessionFixationProtectionFilter") BroadleafMultiSessionFixationProtectionFilter filter ) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }
    
    @Configuration
    public class SiteSecurityConfig extends WebSecurityConfigurerAdapter {
    ...
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
            ...
                .addFilterBefore(multiSessionFixationProtectionFilter, SessionManagementFilter.class)
            ...
        }
    ...
    }
    

    Additionally you should remove the SessionFixationProtectionFilter if you have one setup since the MultiSessionFixationProtectionFilter will delegate to the former when appropriate.

  7. Setup the jdbc http session configuration

    XML Configuration:

    Create a new file: site/src/main/webapp/WEB-INF/applicationContext-session.xml with the following contents:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:jdbc="http://www.springframework.org/schema/jdbc"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:annotation-config/>
    
        <bean class="com.broadleafcommerce.enterprise.csr.web.http.BroadleafJdbcHttpSessionConfiguration">
            <constructor-arg name="dataSource" ref="webDS"/>
        </bean>
    
    </beans>
    

    Then add the reference to this new app context in your RootContextConfiguration (after applicationContext-filter.xml)

    /WEB-INF/applicationContext-session.xml
    

    Java Configuration:

    Add this import to your servlet configuration class:

    @Import(BroadleafJdbcHttpSessionConfiguration.class)
    

    If you are not using the JDBC backed session repository or you don't want to use Broadleaf's configuration then you'll need add the BroadleafMultiSessionCsrConfig and SpringHttpSessionConfiguration in the same manner as mentioned above for BroadleafJdbcHttpSessionConfiguration

  8. Finally, in your site's footer.html add the following JS dependencies:

    <th:block th:if="${#brc.csrMode AND #props.get('csr.multi.customer.session.enabled')}">
        <blc:form>
            <input type="hidden" name="csrSessionAlias" th:value="${#httpServletRequest.getParameter('_s') != null ? #httpServletRequest.getParameter('_s') : 0}"/>
        </blc:form>
        <blc:bundle name="csrMultiSession.js"
                    mapping-prefix="/js/"
                    files="lib/URI.js,
                           csr-multi-client.js" />
    </th:block>
    
  9. If you are using a JDBC Backed Repo, you can initialize a spring-enabled scheduler to clean up any inactive sessions.
    JdbcOperationsSessionRepository contains an @Scheduled method to clear expired sessions. You can enable this by adding the following to your applicationContext-session.xml:

    <!-- configure scheduler to clean up expired csr sessions -->
    <task:annotation-driven scheduler="myMultiCsrSessionScheduler"/>
    <task:scheduler id="myMultiCsrSessionScheduler" pool-size="10"/>
    

Things to be aware of

If your application communicates with any external integrations (e.g. Payment Gateways) that rely on re-directs as a mechanism of transferring information.
You'll also need to make sure you extend and override those configurations to take into account the new multi-session request parameter.
For example, if your payment gateway relies on a transparent redirect to complete checkout, then you would need to make sure that the application constructs the appropriate "session aware" url:

String url = BLCSystemProperty.resolveSystemProperty("gateway.mygateway.configuration.processUrl");
url = multiSessionHelperService.encodeCurrentAliasToUrl(url);
return url;