4
votes

I am working on an existing code that's working just fine in an existing environment. The application has a login form that takes user to a landing page after they login.

My problem: When I moved the application to AWS Beanstalk (with 2 instances), successful login brings the user back to the login page

The application is Spring-based (MVC, Security), with the following configuration for security:

<security:http use-expressions="true">
    <security:intercept-url pattern="/" access="permitAll" />
    <security:intercept-url pattern="/index.html" access="permitAll" />
    <security:intercept-url pattern="/login.html" access="permitAll" />

    ... bunch of other pages ....

    <security:intercept-url pattern="/secure/**" access="isAuthenticated()" />


    <security:form-login login-page="/login.html"
        default-target-url="/secure/landing.html"
        authentication-failure-url="/login.html?login_error=1" />

    <security:logout logout-url="/logout.html"
        logout-success-url="/login.html" />
</security:http>

The application is working fine in a single node environment, example log trace when user logs in:

1: [http-bio-8080-exec-1 DEBUG DefaultRedirectStrategy - sendRedirect - Redirecting to '/myapp/secure/landing.html'

2: [http-bio-8080-exec-1] DEBUG HttpSessionSecurityContextRepository - saveContext - SecurityContext stored to HttpSession: 'org.springframework.security.core.context.SecurityContextImpl@86073c69: /* some details */ Granted Authorities: ROLE_USER'

3: [http-bio-8080-exec-1] DEBUG SecurityContextPersistenceFilter - doFilter - SecurityContextHolder now cleared, as request processing completed

4: [http-bio-8080-exec-1] DEBUG AntPathRequestMatcher - matches - Checking match of request : '/secure/landing.html'; against '/resources/**'

When this application (we took the exact same war file) into AWS Beanstalk environment, configured with 2 instances, this is what happen:

1: [http-bio-8080-exec-7] DEBUG DefaultRedirectStrategy - sendRedirect - Redirecting to '/secure/landing.html'

2: [http-bio-8080-exec-7] DEBUG HttpSessionSecurityContextRepository - createNewSessionIfAllowed - HttpSession being created as SecurityContext is non-default

3: [http-bio-8080-exec-7] DEBUG HttpSessionSecurityContextRepository - saveContext - SecurityContext stored to HttpSession: 'org.springframework.security.core.context.SecurityContextImpl@a3421210: /* some details */ Granted Authorities: ROLE_USER'

4: [http-bio-8080-exec-7] DEBUG SecurityContextPersistenceFilter - doFilter - SecurityContextHolder now cleared, as request processing completed

5: [http-bio-8080-exec-9] DEBUG AntPathRequestMatcher - matches - Checking match of request : '/login.html'; against '/resources/**'

Line-by-line comparison:

  • The AWS log has 1 extra line, line 2
  • The AWS log shows thread change from exec-7 to exec-9 in the last line, line 5 (and hence it loses the original request redirection and replace it back to /login.html rather than /secure/landing.html)

To get around the problem that's going on in AWS, re-configuring beanstalk to only work with 1 instance seems to hide the problem away.

Any idea what's missing from the configuration that fails in a multi node environment?

2
What are you using to do the load balancing between instances? Elastic Load Balancer?Rob Blake
Using beanstalk (set to "Load balancing, autoscaling"), so yes, I think it automatically comes with elastic load balancer? In the beanstalk configuration, I work around the problem by setting "Minimum instance count" to 1, it ran into problem when it was set to 2TS-
In your Elastic Load Balancer configuration, are you using sticky sessions with Application generated cookies? In particular are you setting the load balancer to use JSESSIONID as the cookie to use for application sticky sessions? See here: docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/…Rob Blake
@RobBlake: Slightly different from the link you sent because this is through Beanstalk, but yes, seems like configuring "sticky session" fix it. Thanks!TS-

2 Answers

4
votes

When using load balancer, you have to keep in mind, that every request can end up on a different instance. In this case any information stored on server 1 will not be available to server 2. In case of user authentication there are several ways to solve this:

  • Use third party for maintaining authentication info.
  • Use sticky sessions - configure load balancer to always forward to one server for the duration of the http session
0
votes

According to the Spring Security documentation you need to make sure your SecurityContextHolder bean is setup using a non-ThreadLocal mode. By default, I believe it is setup using MODE_THREADLOCAL which does not seem to persist across the threading system of the elastic beanstalk setup.

Inject strategyName = SecurityContextHolder.MODE_INHERITABLETHREADLOCAL or MODE_GLOBAL into your SecurityContextHolder bean and let me know if that works.