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 (http://projects.spring.io/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/1.2.1.RELEASE/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 (http://docs.spring.io/spring-session/docs/1.2.1.RELEASE/api/org/springframework/session/web/http/CookieHttpSessionStrategy.html).
to maintain session aliases between requests. Since a single session may contain many sesssion 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 ammend 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 inapplicationContext-security.xml
For example:
...
<sec:intercept-url pattern="/" requires-channel="https"/>
<sec:intercept-url pattern="/**" requires-channel="https"/>
...
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
- If you are using a JDBC backed repo to store the session attributes, you must include the following
dependency in your root
pom.xml
<!-- Needed for Spring Session -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.1.9.RELEASE</version>
</dependency>
- 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/master/spring-session/src/main/resources/org/springframework/session/jdbc
Notice that to keep convention with the framework, we've prefixed the "oob spring" table names with
BLC_
and you should do the same.
DROP TABLE BLC_SPRING_SESSION_ATTRIBUTES IF EXISTS;
DROP TABLE BLC_SPRING_SESSION IF EXISTS;
CREATE TABLE BLC_SPRING_SESSION (SESSION_ID CHAR(36),CREATION_TIME BIGINT NOT NULL,LAST_ACCESS_TIME BIGINT NOT NULL,MAX_INACTIVE_INTERVAL INT NOT NULL,PRINCIPAL_NAME VARCHAR(100),CONSTRAINT BLC_SPRING_SESSION_PK PRIMARY KEY (SESSION_ID));
CREATE INDEX BLC_SPRING_SESSION_IX1 ON BLC_SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE BLC_SPRING_SESSION_ATTRIBUTES (SESSION_ID CHAR(36),ATTRIBUTE_NAME VARCHAR(200),ATTRIBUTE_BYTES LONGVARBINARY,CONSTRAINT BLC_SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),CONSTRAINT BLC_SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES BLC_SPRING_SESSION(SESSION_ID) ON DELETE CASCADE);
CREATE INDEX BLC_SPRING_SESSION_ATTRIBUTES_IX1 ON BLC_SPRING_SESSION_ATTRIBUTES (SESSION_ID);
- To enable multi-session assisted shopping in your application, you must set the following system property:
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
- In
site/src/main/webapp/WEB-INF/applicationContext-filter.xml
:
- add
blCSRMultiSessionInitFilter
as the very first filter in yourblPreSecurityFilterChain
- add
blCSRMultiSessionCleanupFilter
as the very last filter in yourblPostSecurityFilterChain
- In
site/src/main/webapp/WEB-INF/applicationContext-filter.xml
declare the following beans at the bottom:
<bean id="blMultiSessionHelperService" class="com.broadleafcommerce.enterprise.csr.web.filter.BroadleafMultiSessionHelperServiceImpl"/>
<bean id="blCSRMultiSessionInitFilter" class="com.broadleafcommerce.enterprise.csr.web.filter.BroadleafCSRMultiSessionInitFilter"/>
<bean id="blCSRMultiSessionCleanupFilter" class="com.broadleafcommerce.enterprise.csr.web.filter.BroadleafCSRMultiSessionCleanupFilter"/>
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
.
In
site/src/main/webapp/WEB-INF/applicationContext-security.xml
make sure all intercept-urls require either allHTTPS
or allHTTP
see requirements section above for details.In
site/src/main/webapp/WEB-INF/applicationContext-security.xml
changeblSessionFixationProtectionFilter
to be:
<sec:custom-filter ref="blMultiSessionFixationProtectionFilter" before="SESSION_MANAGEMENT_FILTER"/>
and declare the bean:
<bean id="blMultiSessionFixationProtectionFilter" class="com.broadleafcommerce.enterprise.csr.web.filter.BroadleafMultiSessionFixationProtectionFilter"/>
- In
site/src/main/webapp/WEB-INF/applicationContext-servlet.xml
add the following bean:
<bean class="com.broadleafcommerce.enterprise.csr.web.extension.MultiSessionTemplateResolverExtensionHandler"/>
- 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 id="blCookieHttpSessionStrategy" class="com.broadleafcommerce.enterprise.csr.web.http.BroadleafCookieHttpSessionStrategy"/>
<bean class="com.broadleafcommerce.enterprise.csr.web.http.BroadleafJdbcHttpSessionConfiguration">
<property name="httpSessionStrategy" ref="blCookieHttpSessionStrategy"/>
<property name="dataSource" ref="webDS"/>
<property name="tableName" value="BLC_SPRING_SESSION"/>
</bean>
</beans>
- Add the reference to this new app context in
site/src/main/webapp/WEB-INF/web.xml
(after applicationContext-filter.xml)
/WEB-INF/applicationContext-session.xml
- 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>
- 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 yourapplicationContext-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;