1

I have "Vaadin 10 with Spring Boot" application. I want to allow user to access application from one place at a time. So I used maximumSessions(1). Example, from Chrome browser I have logged in with user "XYZ". Now with the same user (i.e. "XYZ") I tried to login to Opera browser. So as per configuration shown below, it will expire session of Chrome browser but it is not redirecting to "/login". It shows message "Invalid JSON response from Server". Below is the Spring security configuration:

@Override
protected void configure(HttpSecurity http) throws Exception {
    // Not using Spring CSRF here to be able to use plain HTML for the login page
    http.csrf().disable()

            // Register our CustomRequestCache, that saves unauthorized access attempts, so
            // the user is redirected after login.
            .requestCache().requestCache(new CustomRequestCache())

            // Restrict access to our application.
            .and().authorizeRequests()
            .antMatchers("/ForgetPassword","/ChangePassword","/login").permitAll()
            // Allow all flow internal requests.
            .requestMatchers(SecurityUtils::isFrameworkInternalRequest).permitAll()

            // Allow all requests by logged in users.
            .anyRequest().authenticated()
            // Configure the login page.
            .and().formLogin().loginPage("/login").permitAll().loginProcessingUrl("/login")
            .failureUrl("/login?error")

            // Register the success handler that redirects users to the page they last tried
            // to access
            .successHandler(new SavedRequestAwareAuthenticationSuccessHandler())

            // Configure logout
            .and().logout().logoutSuccessUrl(LOGOUT_SUCCESS_URL)
            .deleteCookies("JSESSIONID")
            //.invalidateHttpSession(true)
            .and()
            .sessionManagement()
            //.invalidSessionUrl("/login")
            .maximumSessions(1)
            //.maxSessionsPreventsLogin(false)
            .sessionRegistry(sessionRegistry())
            .expiredUrl("/login");
Vedran Pavic
  • 2,339
  • 2
  • 27
  • 31
Harita Parmar
  • 75
  • 1
  • 1
  • 9

1 Answers1

2

The problem is that, by redirecting the request, Vaadin receives the login page as a response to a internal request.

This seems to work:

        .expiredSessionStrategy(e -> {
            final String redirectUrl = e.getRequest().getContextPath() + "/login";
            if(SecurityUtils.isFrameworkInternalRequest(e.getRequest())) {
                e.getResponse().setHeader("Content-Type", "text/plain");
                e.getResponse().getWriter().write("Vaadin-Refresh: " + redirectUrl);
            } else {
                e.getResponse().sendRedirect(redirectUrl);
            }
        });

It is documented in the ConnectionStateHandler

public interface ConnectionStateHandler {

/**
 * A string that, if found in a non-JSON response to a UIDL request, will
 * cause the browser to refresh the page. If followed by a colon, optional
 * whitespace, and a URI, causes the browser to synchronously load the URI.
 *
 * <p>
 * This allows, for instance, a servlet filter to redirect the application
 * to a custom login page when the session expires. For example:
 * </p>
 *
 * <pre>
 * if (sessionExpired) {
 *     response.setHeader(&quot;Content-Type&quot;, &quot;text/html&quot;);
 *     response.getWriter().write(myLoginPageHtml + &quot;&lt;!-- Vaadin-Refresh: &quot;
 *             + request.getContextPath() + &quot; --&gt;&quot;);
 * }
 * </pre>
 */
String UIDL_REFRESH_TOKEN = "Vaadin-Refresh";
Tulio
  • 432
  • 2
  • 4
  • Hi Tulio, I tried above code. **It works in below condition:** step-1: login in browser chrome with user1. Step-2: login with the same user from mozilla. Step-3: click on any button in chrome browser. That works. **But it doesn't works in below condition:** step-1: login in browser chrome with user1. Step-2: login with the same user from mozilla. Step-3: reload page in chrome browser. It gives NullPointerException at line `.expiredSessionStrategy(e -> e.getRequest().getSession(false).invalidate())` – Harita Parmar Aug 24 '18 at 05:38
  • Hi Harita. I changed the answer to cover that condition. – Tulio Aug 24 '18 at 11:56
  • Hi Tulio...Thank you so much for your response !! I tried above code. The only problem is: It is redirecting the response to **http://localhost:8080/login**. But it should redirect to **http://localhost:8181/AppName/login**. Please advise. – Harita Parmar Aug 27 '18 at 05:02
  • I added the redirectUrl variable to the answer, so the redirect is context sensitive. I could not reproduce the changing of the port from localhost:8181 to localhost:8080 though. Are you redirecting to the same web application? – Tulio Aug 27 '18 at 09:58
  • Hurrey....It works :) Thank you so much for your great help. Sorry...It was my mistake to write different ports. I need to redirect to same web application only. Once again Thank you very much :) – Harita Parmar Aug 27 '18 at 14:52