Developing Spring Web Flow 2 Application part 4 – Spring Security and HTTPS

As part of “Developing Spring Web Flow 2 Application” series, this post shows the following features:

  • Secure a view with Spring Security
  • Switching between HTTP and HTTPS connections




I. Overview

In the Shopping Cart application, there are two places where we would like to secure our application.

  • Login action/flow: user provides username/password.
  • Checkout flow: user can enter this flow only when user is both authenticated and authorized.



In order to protect the login information as well as information within the flow, we would like to use HTTPS during login process and within the flow. As soon as login information is verified or execution exits the flow, we would like to switch back to use HTTP.




II. Secure a flow

The process to secure a flow execution involves three steps.

  • Annotate the flow definition with the secured element
  • Configure Spring Security with authentication and authorization rules
  • Configure a SecurityFlowListener to process security rules



Annotate the flow
To annotate either a flow, state or transition to require authorization check before entering, we can use the secured element.

<view-state id="checkout" view="options" model="order"> <secured attributes="ROLE_USER"/> ... </view-state>



The attributes attribute is a comma separated list of Spring Security authorization attributes.




Configure Spring Security

Here is a portion of configuration file for setting up HTTP and Authentication Provider. Note that we specify specific login and logout pages instead of using the ones that Spring Security provides. We also specify the login-url, authentication-failure-url, default-target-url and logout-success-url.

<http auto-config="true">                 <form-login login-page="/account/login" login-processing-url="/j_spring_security_check" default-target-url="/index.html" authentication-failure-url="/account/login" /> <logout logout-url="/account/logout" logout-success-url="/account/logoutSuccess" /> </http>



For user authentication, we use custom SQL statements to authenticate user against the database.

<authentication-provider> <password-encoder hash="md5" /> <jdbc-user-service data-source-ref="dataSource" users-by-username-query= "SELECT username, password, 'true' as enabled FROM account WHERE username = ?" authorities-by-username-query= "SELECT account.username, authorities.authority as authorities FROM account, authorities WHERE account.username = ? AND account.username = authorities.username" /> </authentication-provider>



In the web.xml file, we also need to configure a filter to intercept all requests

<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>



Configure SecurityFlowListener

We also need to configure a SecurityFlowListener bean and have it referenced by the flow executor.

<bean id="securityFlowExecutionListener" class="org.springframework.webflow.security.SecurityFlowExecutionListener" /> <webflow:flow-executor id="flowExecutor"> <webflow:flow-execution-listeners> <webflow:listener ref="securityFlowExecutionListener" /> </webflow:flow-execution-listeners> </webflow:flow-executor>



III. Access Login page directly

Although the secured element gives you the ability to secure a flow or state, there are also times when you would like to let the user to access the login page directly. For example, in the home.jsp page, we provide a link to allow the user to login into our application. In this case, login page is accessed directly as opposed to be invoked by Spring because of the secured element.

<a href="account/login">Login</a>



There is one catch with this approach, once this link is clicked, the execution will exit the current flow and go to the login page, and when login process is finished by user clicking the submit button, although the execution will then go to the target page, a new flow will be created as well. The side effect is that whatever variables stored in the scope (ex: conversation scope) before login will be lost in the new flow. We need a way to prevent variables from getting lost. Another disadvantage of this approach is that there is no easy way to specify the desired target page after the login process, login link may appear at multiple places within the application.

Fortunately, there is a way around that.

The idea is to use an action-state element and the secured element.

To be more specific, the link to login page is created as if it is a link to transition to another state. For example, in the home view-state, first we designate a transition on login event to a login action-state.

<view-state id="home" view="home"> ... <transition on="login" to="login" /> </view-state>



Then, we create an action-state named login and secure this state with the secured element. By doing so, we ensure the flow is transitioned from the home view-state to another state within the same flow and the actual login process is still invoked by Spring.

Because action-state requires an action, and also because we would like to retrieve the user information from the backend service by using the login information, here we use an evaluate element to invoke backend service.

During the login process, if username/password information is incorrect, the error message will show up in the login page. If an error is thrown from the backend service, we specify a transition on error event to the error view-state where error message is displayed to the user.

If backend service returns ok, a transition on success event to loginSuccess view-state is triggered. When all is done, transition is back to the home view-state.

<action-state id="login"> <secured attributes="ROLE_USER"/> <evaluate expression="accountServiceAction.getAccount"/> <transition on="success" to="loginSuccess" /> <transition on="error" to="error" /> </action-state> <view-state id="loginSuccess" view="account/loginSuccess" > <transition on="loginEnd" to="home" /> </view-state> <view-state id="error" view="error"> <transition to="home" /> </view-state>



Finally, the link in the home.jsp page now becomes

<a href="${flowExecutionUrl}&_eventId=login">Login </a>.



IV. Switching between HTTP and HTTPS

So far, all the communication between client and server is using HTTP, but there are situations where we would like to use HTTPS.

Unlike the secured element which checks for authentication and authorization, there is no standard way provided by Spring Web Flow to declare a view or flow to use either HTTP or HTTPS.

Spring Security already provides an easy way to declare which URLs should use HTTP or HTTPS. The difficulty of applying this to web flow is because all URLs created by web flow are almost the same except the execution key. In other words, there is no easy way to specify a unique URL of a web flow application so that we can use this unique URL when configuring Spring Security.

Fortunately again, there is also a way around that.

The idea is to find unique names to mark the start and end of the flow and use these names in Spring Security configuration.

Take checkout flow as an example, we design the flow such that the flow starts when checkout event is triggered while in the viewCart view-state and flow ends when continue event is triggered. These two events are then used as our unique names in Spring Security configuration.

The link to start the checkout flow looks like this

<a href="${flowExecutionUrl}&_eventId=checkout">checkout</a>



The link to end the checkout flow looks like this

<a href="${flowExecutionUrl}&_eventId=continue">Continue </a>



In Spring Security configuration, http element can take the path-type attribute and the default is to treat all expressions as Apache Ant paths. The problem with that is that Spring Security will ignore query string, therefore configuration like these won’t work as we expect.

<http auto-config="true">                 <intercept-url pattern="**/*&eventId=checkout" requires-channel="https"/> <intercept-url pattern="**/*&eventId=continue" requires-channel="http"/>



The solution is to use regular expression.

<http auto-config="true" path-type="regex">                 <!-- checkout flow: enter https --> <intercept-url pattern="\A.*checkout\Z" requires-channel="https"/> <!-- checkout flow: exit https --> <intercept-url pattern="\A.*continue\Z" requires-channel="http"/>



If you apply these, you will see communication switch from HTTP to HTTPS when entering the checkout flow and switch back to HTTP again after leaving the flow. There is just one problem. The login information is lost after switching back to HTTP.

The catch here is that Tomcat server will set the JSESSIONID cookie as secure when entering into HTTPS, therefore when we later switch out from HTTPS to HTTP, the browser will drop this secure JESSIONID cookie (not sending this cookie back to server), tomcat server will then create a new JSESSIONID cookie (not secure one), and this is why we lost the SecurityContext information.

Not only do we lose SecurityContext, if our shopping cart already contains some items, we will also lose those items because of this new created cookie.

One solution to the SecurityContext problem is to use the REMEMBER ME feature provided by Spring Security.

By using auto-config=”true” attribute, we already have the “remember-me” service enabled automatically. Next thing to do is to provide a check box in the login page to allow user to enable it.

<input type="checkbox" name="_spring_security_remember_me" id="remember_me" />



Once this is configured and checked, Spring will set a cookie named

SPRING_SECURITY_REMEMBER_ME_COOKIE



in the HTTP response and browser will use this cookie in the following requests.

However, forcing user to enable “REMEMBER ME” feature may not always be possible. We need another solution.

User csw199 from Spring forum discussed this same problem and provided a nice solution which is to use a custom Filter to override JSESSIONID cookie set by Tomcat.

A simple custom filter may look like this

package com.shoppingcart.web.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class CookieFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { final HttpServletRequest httpRequest = (HttpServletRequest) request; final HttpServletResponse httpResponse = (HttpServletResponse) response; final HttpSession session = httpRequest.getSession(false); if (session != null) { final Cookie sessionCookie = new Cookie("JSESSIONID", session.getId()); sessionCookie.setMaxAge(-1); sessionCookie.setSecure(false); sessionCookie.setPath(httpRequest.getContextPath()); httpResponse.addCookie(sessionCookie); } chain.doFilter(request, response); } @Override public void init(FilterConfig arg0) throws ServletException { } }



and it is very important to enable this filter at the very beginning of web.xml file.

<filter> <filter-name>cookieFilter</filter-name> <filter-class>com.shoppingcart.web.filter.CookieFilter</filter-class> </filter> <filter-mapping> <filter-name>cookieFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>



Advertisements
Developing Spring Web Flow 2 Application part 4 – Spring Security and HTTPS

20 thoughts on “Developing Spring Web Flow 2 Application part 4 – Spring Security and HTTPS

  1. While on the viewCart page,attempting to click on the checkout link so as to be taken to this view,i get the following errors

    Exception thrown in state ‘null’ of flow ‘checkout’

    java.lang.IllegalStateException: No value for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@f9c26e] bound to thread [http-127.0.0.1-8443-1]

    anyone who might have an idea of what would be wrong?

  2. Lyndon Smith says:

    Hi

    Nice tutorial but don’t you get the impression that you have to fight Web Flow all the way.

    The rest of Spring is a joy to use but Web Flow…

    It still needs some work, particularly the https part!

  3. Anonymous says:

    I have the same question with first one.
    the error shows as follows:
    FlowExecutionException: Exception thrown within inactive flow ‘shopping’

    anyone who might have an idea of what would be wrong?

  4. Was says:

    Hi,

    Thanks for this tutorial. I tried to secure my flows but for other authorities :

    In my UserDetailsServiceImpl i check that the loged user has “SOME_OTHER_AUTH” in his grantedAuthorities.

    I also find it in the UserContext role. But when I try to accs to my secured flow, i have the acces denied page.

    It seems to be that the secured tag works only for some default authorities (eg: ROLE_USER, ROLE_ADMIN ..)

    Have I missed something of have I to override something to make my flow recognize my custom authority ?

    Thank you

  5. cchweblog says:

    @Was
    I wish I can be more helpful but I haven’t worked on web flow since I finished the post. Not sure how I can help you figure out the problem.

  6. Nach den ersten drei Neuverpflichtungen u.a. Manuel Neuer vom
    FC Schalke 04, der als größter Transfercoup dieses Jahres gehandelt wird, ist der FC Bayern München an weiteren talentierten Spielern, vor allem in der Defensive, interessiert.

  7. Wir haben mittlerweile die ersten Spezialitäten wie Olivenöl,
    Antipasto-Spezialitäten und Saucen für Pasta im Sortiment und weitere werden bald
    folgen.

  8. Die Abwechslung zwischen Ruhe und Bewegung ist neben der körperlichen Fitness ebenfalls ein gutes Stoffwechseltraining.

  9. Treppensteigen, Spaziergänge, Fahrradfahren zur Arbeit sind gute
    Möglichkeiten, die Bewegung in den Berufsalltag einzubauen.

  10. Um noch genauer sagen zu können, wie sich Bewegung auf die Krebsentstehung auswirkt, muss allerdings noch weiter geforscht werden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s