Documentation

1.6 to 2.0 Security Migration

One key difference between Broadleaf versions 1.6 and 2.0 is the upgrade from Spring 3.0 to Spring 3.1. This includes Spring Security. The changes to the Spring Security configuration are fairly minimal for most applications. First, it is assumed that you have updated your Maven dependencies to include the 2.0 version of the Broadleaf Framework. This should pull in the transitive dependencies, including Spring Security 3.1.

First, make sure that you are using Spring 3.1 namespaces in the Spring Security application context resources:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:sec="http://www.springframework.org/schema/security"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security-3.1.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.1.xsd">

Site War

In 1.6, if you used the Broadleaf Maven Archetype for generating a Broadleaf project, then you will have a project called site-war. The Spring Security file for site-war is located at site-war/src/main/webapp/WEB-INF/applicationContext-security.xml. This should be updated to include the new configuration, which looks like this:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:sec="http://www.springframework.org/schema/security"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security-3.1.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.1.xsd">

    <context:component-scan base-package="org.broadleafcommerce.common.web.security"/>
    <context:component-scan base-package="org.broadleafcommerce.profile.web.core.security"/>
    <context:component-scan base-package="org.broadleafcommerce.core.web.order.security"/>

    <!-- Resources do not need security -->
    <sec:http pattern="/css/**" security="none" />
    <sec:http pattern="/img/**" security="none" />
    <sec:http pattern="/js/**" security="none" />       
    <sec:http pattern="/favicon.ico" security="none" />       
    <sec:http pattern="/robots.txt" security="none" />       

    <!-- Set up Spring security for the application -->
    <sec:http auto-config="false" authentication-manager-ref="blAuthenticationManager" >
        <!-- We handle session fixation protection ourselves  -->
        <sec:session-management session-fixation-protection="none" />

        <!-- Specify these URLs as requiring HTTPS to encrypt user data  -->
        <sec:intercept-url pattern="/register*" requires-channel="https" />
        <sec:intercept-url pattern="/login*/**" requires-channel="https"  />
        <sec:intercept-url pattern="/account/**" access="ROLE_USER" requires-channel="https" />
        <sec:intercept-url pattern="/checkout/**" requires-channel="https" />
        <sec:intercept-url pattern="/confirmation/**" requires-channel="https" />

        <!-- Since the cart page is viewing as a modal, we want to allow it on any page -->
        <sec:intercept-url pattern="/cart/**" requires-channel="any" />

        <!-- All URLs not explicitly specified as https will be served under http -->
        <sec:intercept-url pattern="/" requires-channel="http"/>
        <sec:intercept-url pattern="/**" requires-channel="http"/>

        <!-- Define the login form along with the success and failure handlers -->
        <sec:form-login login-page='/login'
            authentication-success-handler-ref="blAuthenticationSuccessHandler"
            authentication-failure-handler-ref="blAuthenticationFailureHandler"
            login-processing-url="/login_post.htm" 
        />

        <!-- Provide the logout handler -->
        <sec:logout delete-cookies="ActiveID" invalidate-session="true" logout-url="/logout"/>

        <!-- Specify our custom filters -->
        <sec:custom-filter ref="blCustomerStateFilter" after="REMEMBER_ME_FILTER"/>
        <sec:custom-filter ref="blCartStateFilter" before="ANONYMOUS_FILTER"/>
        <sec:custom-filter ref="blSessionFixationProtectionFilter" before="SESSION_MANAGEMENT_FILTER"/>
    </sec:http>

    <!--  The BLC Authentication manager.   -->
    <sec:authentication-manager alias="blAuthenticationManager">
        <sec:authentication-provider user-service-ref="blUserDetailsService">
            <sec:password-encoder ref="blPasswordEncoder" />
        </sec:authentication-provider>
    </sec:authentication-manager>

    <!--  User details service that authenticates using customer data in the database. -->
    <sec:jdbc-user-service data-source-ref="webDS"
        id="blUserDetailsService"
        users-by-username-query="SELECT USER_NAME,PASSWORD,TRUE FROM BLC_CUSTOMER WHERE USER_NAME=?"
        authorities-by-username-query="SELECT c.USER_NAME,r.ROLE_NAME from BLC_CUSTOMER c 
                                          JOIN BLC_CUSTOMER_ROLE cr ON c.CUSTOMER_ID = cr.CUSTOMER_ID 
                                          JOIN BLC_ROLE r ON cr.ROLE_ID = r.ROLE_ID 
                                          WHERE USER_NAME=?" />

    <!-- Sets the login failure URL -->
    <bean id="blAuthenticationFailureHandler" class="org.broadleafcommerce.common.security.BroadleafAuthenticationFailureHandler">
        <constructor-arg value="/login?error=true" />
        <property name="redirectStrategy" ref="blAuthenticationFailureRedirectStrategy" />
    </bean>

    <!-- Sets the login success URL -->
    <bean id="blAuthenticationSuccessHandler" class="org.broadleafcommerce.core.web.order.security.BroadleafAuthenticationSuccessHandler">
        <property name="redirectStrategy" ref="blAuthenticationSuccessRedirectStrategy" />
        <property name="defaultTargetUrl" value="/account" />
        <property name="alwaysUseDefaultTargetUrl" value="true" />
    </bean>

</beans>

This effectively provides a simple JDBC User Service that pulls the customer and roles from the Broadleaf Database. It also provides a standard Spring Authentication Manager that uses the JDBC User Details Service to authenticate. You'll notice a section that removes all security for certain resources:

<!-- Resources do not need security -->
<sec:http pattern="/css/**" security="none" />
<sec:http pattern="/img/**" security="none" />
<sec:http pattern="/js/**" security="none" />       
<sec:http pattern="/favicon.ico" security="none" />       
<sec:http pattern="/robots.txt" security="none" />

You might need to add or modify this section if you want to ensure that no security is required for certain additional resources. You'll also notice a section that defines all of the resources that should be secured:

<!-- Set up Spring security for the application -->
    <sec:http auto-config="false" authentication-manager-ref="blAuthenticationManager" >
        <!-- We handle session fixation protection ourselves  -->
        <sec:session-management session-fixation-protection="none" />

        <!-- Specify these URLs as requiring HTTPS to encrypt user data  -->
        <sec:intercept-url pattern="/register*" requires-channel="https" />
        <sec:intercept-url pattern="/login*/**" requires-channel="https"  />
        <sec:intercept-url pattern="/account/**" access="ROLE_USER" requires-channel="https" />
        <sec:intercept-url pattern="/checkout/**" requires-channel="https" />
        <sec:intercept-url pattern="/confirmation/**" requires-channel="https" />

        <!-- Since the cart page is viewing as a modal, we want to allow it on any page -->
        <sec:intercept-url pattern="/cart/**" requires-channel="any" />

        <!-- All URLs not explicitly specified as https will be served under http -->
        <sec:intercept-url pattern="/" requires-channel="http"/>
        <sec:intercept-url pattern="/**" requires-channel="http"/>

        <!-- Define the login form along with the success and failure handlers -->
        <sec:form-login login-page='/login'
            authentication-success-handler-ref="blAuthenticationSuccessHandler"
            authentication-failure-handler-ref="blAuthenticationFailureHandler"
            login-processing-url="/login_post.htm" 
        />

        <!-- Provide the logout handler -->
        <sec:logout delete-cookies="ActiveID" invalidate-session="true" logout-url="/logout"/>

        <!-- Specify our custom filters -->
        <sec:custom-filter ref="blCustomerStateFilter" after="REMEMBER_ME_FILTER"/>
        <sec:custom-filter ref="blCartStateFilter" before="ANONYMOUS_FILTER"/>
        <sec:custom-filter ref="blSessionFixationProtectionFilter" before="SESSION_MANAGEMENT_FILTER"/>
    </sec:http>

If you are using the standard Broadleaf Authentication Provider (e.g. the JDBC User Details Service), this is the only other section that you might need to change. This section provides several functions. First, it uses the configured Authentication Manager to provide the authentication mechanism. It defines how sessions should be managed. Broadleaf provides session management and session fixation via the blSessionFixationProtectionFilter. This section also specifies which URLs should be protected by HTTPS. A login form is defined to tell Spring where to direct the user if they require login. A logout handler is defined, which tells Spring to delete a cookie, invalidate the session, and which page to take the user to after logout. Finally a list of custom filters are defined. These filters should not be removed as they are important to the correct operation of Broadleaf Commerce.

Admin War

The Admin configuration is slightly different. Because Broadleaf uses GWT for the Administrative Console, Broadleaf can provide more granular security for Admin functions. Spring Security is still used to lock down and manage website security. However, the user roles and permissions are more important than with the Site War. The Site War only deals with Customer security. Customers are either logged in or not, and if they are, they have a role called ROLE_USER. However, with the Admin Console, the administrator(s) can have any number of roles and permissions allowing them to perform certain tasks and restricting others. Broadleaf uses a combination of Spring Security and customer security to accomplish this. If you used the Broadleaf Maven Archetype to build your 1.6 project, you will want to modify your admin-war/src/main/webapp/WEB-INF/applicationContext-admin-security.xml. Here is the new configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:sec="http://www.springframework.org/schema/security"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security-3.1.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.1.xsd">

    <context:component-scan base-package="org.broadleafcommerce.profile.web.core.security"/>
    <context:component-scan base-package="org.broadleafcommerce.core.web.order.security"/>

    <!-- Resources do not need security -->
    <sec:http pattern="/css/**" security="none" />
    <sec:http pattern="/images/**" security="none" />
    <sec:http pattern="/js/**" security="none" />       
    <sec:http pattern="/utility.service" security="none" />       
    <sec:http pattern="/com.mycompany.gwt.mycompanyAdmin/remote_logging/remote_logging" security="none" />       
    <sec:http pattern="/favicon.ico" security="none" />       
    <sec:http pattern="/robots.txt" security="none" />       

    <!-- Set up Spring security for the application -->
    <sec:http auto-config="false" authentication-manager-ref="blAdminAuthenticationManager" >
        <!-- All admin URLs will be secured with https -->
        <sec:intercept-url pattern="/**" requires-channel="https" />
        <sec:intercept-url pattern="/" requires-channel="https" />

        <!-- Define the login form along with the success and failure handlers -->
        <sec:form-login login-page='/blcadmin/login.jsp'
            authentication-success-handler-ref="blAdminAuthenticationSuccessHandler"
            authentication-failure-handler-ref="blAdminAuthenticationFailureHandler"
            login-processing-url="/admin/login_admin_post" 
        />

        <!-- Provide the logout handler -->
        <sec:logout invalidate-session="true" logout-url="/adminLogout.htm" logout-success-url="/admin.html"/>

        <!-- Since we run this alongside the site, we need to set up the special port mapping -->
        <!-- This isn't necessary for site becauase it maps from 8080 to 8443, which is a default mapping -->
        <sec:port-mappings>
            <sec:port-mapping http="8081" https="8444"/>
        </sec:port-mappings>

        <!-- Specify our custom filters -->
        <sec:custom-filter ref="blAdminFilterSecurityInterceptor" after="EXCEPTION_TRANSLATION_FILTER"/>
    </sec:http>

    <!-- The BLC Admin authentication manager -->
    <sec:authentication-manager alias="blAdminAuthenticationManager">
        <sec:authentication-provider user-service-ref="blAdminUserDetailsService">
            <sec:password-encoder hash="plaintext" />
        </sec:authentication-provider>
    </sec:authentication-manager>

    <!-- The service used by the authentication manager -->
    <bean id="blAdminUserDetailsService" class="org.broadleafcommerce.openadmin.server.security.service.AdminUserDetailsServiceImpl"/>

    <!-- This ensures that the user has permissions to perform the requested operation -->
    <bean id="blAdminFilterSecurityInterceptor"
        class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
        <property name="accessDecisionManager" ref="blAccessDecisionManager"/>
        <property name="authenticationManager" ref="blAdminAuthenticationManager" />
        <property name="securityMetadataSource">
            <sec:filter-security-metadata-source>
                <sec:intercept-url pattern="/admin.html*" access="PERMISSION_OTHER_DEFAULT" />
                <sec:intercept-url pattern="/dynamic.entity.service" access="PERMISSION_OTHER_DEFAULT" />
            </sec:filter-security-metadata-source>
        </property>
    </bean>

    <!-- Sets the login failure URL -->
    <bean id="blAdminAuthenticationFailureHandler"
        class="org.broadleafcommerce.common.security.BroadleafAuthenticationFailureHandler">
        <constructor-arg value="/blcadmin/login.jsp?login_error=true" />
    </bean>

    <!-- Sets the login success URL -->
    <bean id="blAdminAuthenticationSuccessHandler"
        class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
        <property name="defaultTargetUrl" value="/admin.html" />
        <property name="alwaysUseDefaultTargetUrl" value="false" />
    </bean>

    <!-- The access decision manager works in conjunciton with global method security -->
    <bean id="blAccessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
        <property name="decisionVoters">
            <list>
                <bean class="org.springframework.security.access.vote.RoleVoter">
                    <property name="rolePrefix" value="PERMISSION_" />
                </bean>
            </list>
        </property>
    </bean>

    <!-- Allow the @Secured annotations -->
    <sec:global-method-security access-decision-manager-ref="blAccessDecisionManager" secured-annotations="enabled" />

</beans>

This is similar to the the Site War configuration. However, there are a few changes. In particular, the list of unsecured resources is a bit different. Notice the following line:

<sec:http pattern="/com.mycompany.gwt.mycompanyAdmin/remote_logging/remote_logging" security="none" />

You will need to change the /com.mycompany.gwt.mycompanyAdmin... to reflect your packaging. You can also change the port mappings if required:

<!-- Since we run this alongside the site, we need to set up the special port mapping -->
<!-- This isn't necessary for site becauase it utilizes a default mapping -->
<!-- Note that since we are providing a special mapping, we must repeat the default ones -->
<sec:port-mappings>
    <sec:port-mapping http="80" https="443"/>
    <sec:port-mapping http="8080" https="8443"/>
    <sec:port-mapping http="8081" https="8444"/>
</sec:port-mappings>

In most cases you should not need to make any additional changes to this file.