Spring security invalid session redirect
Asked Answered
P

3

10

I'm using spring security 4.0.1 inside a spring boot 1.2.3 web application ( and also with spring-session 1.0.1, but this is irrelevant for the case ).

I do have a private area, and an all access area ( "/about", "/","/contact",... more than 20 pages ) for which every user can access. ( it's like a web-shop )

Whenever a logged-in user session expires,Spring detects an invalid session and redirects the user to the '.invalidSessionUrl("/session/error/invalid")'

However, i only want to be redirected if the target link in inside the private area, nor the public one.

How can i avoid that ?

Thanks.

This is my (java) config : ( updated after seen post )

 http
            .authorizeRequests()
            .anyRequest()
                .permitAll()
            .antMatchers("/privado/**")
                .authenticated()
            .and()
                .formLogin()
                .loginPage("/login")
                .failureUrl("/login?error")
                .defaultSuccessUrl("/")
                .successHandler(new SessionSuccessHandler())
            .and()
                .logout()
                .logoutSuccessUrl("/")
                .deleteCookies("JSESSIONID", "SESSION")
            .and()
                .sessionManagement()
                .invalidSessionUrl("/session/error/invalid")
            .sessionFixation()
            .changeSessionId()
            .maximumSessions(1)
            .expiredUrl("/session/error/expired")
            .and()
            .and()
                .csrf()
                .ignoringAntMatchers("/jolokia/**", "/v1.0/**");

How can i achieve that ?

Thanks a lot.

Perversion answered 26/5, 2015 at 17:57 Comment(2)
Look here #22767705Snubnosed
Doesn't work as expected.Perversion
K
4

Another workaround which helped me deal with this issue in similar situation to yours is having an Expired/Invalid session strategy added to your configuration like so:

http
    .expiredSessionStrategy(e -> {
        handleExpiredInvalidSessions(e.getRequest(), e.getResponse());
    })
    .sessionRegistry(sessionRegistry())
    .and()
    .invalidSessionStrategy((request, response) -> {
        handleExpiredInvalidSessions(request, response);
    })

Then you will implement it to match the public URIs and simply forward the request

private void handleExpiredInvalidSessions(HttpServletRequest request, HttpServletResponse response) {
    String requestUri = request.getRequestURI();
    if (isPublicURI(requestUri)) {
        // This will remove the invalid/expired session from the request
        // and prevent the request from failing again
        request.getSession(true).invalidate();
        RequestDispatcher dispatcher = request.getRequestDispatcher(requestUri);
        // Retry the request
        dispatcher.forward(request, response);
    } else {
        // might redirect if you wish
        response.setStatus(440);
    }
}

You still need to implement isPublicURI() depending on your desired public paths, in my case it was only one path so it was quite easy.

Kylakylah answered 29/11, 2019 at 14:20 Comment(0)
J
2

@RobWinch - This seem like a pretty common use case and the solution you propose does not seem to work from the test I ran and also comments. Similar issue was raised I believe in http://forum.spring.io/forum/spring-projects/security/94772-redirect-to-invalid-session-url-only-when-user-accesses-secured-resource and it appears it was never resolved. My thinking is to have multiple http settings (using xml config)

<http pattern="/aboutUs**" security="none" />
<http pattern="/contact**" security="none" />
etc

This does not seem ideal when having quite a number unsecured pages and also adding a new unsecured page requires a configuration update. It will be nice if we can have an "ideal" solution for this use case. With Spring security 4.1 release, it appears there is still no clear way to do this.

Jerriejerrilee answered 18/8, 2016 at 1:27 Comment(0)
V
0

You can provide a custom SessionAuthenticationStrategy to do this. For example:

public class MatcherSessionAuthenticationStrategy implements SessionAuthenticationStrategy {

    private final SessionAuthenticationStrategy delegate;

    private final RequestMatcher matcher;

    public MatcherSessionAuthenticationStrategy(
            SessionAuthenticationStrategy delegate, RequestMatcher matcher) {
        super();
        this.delegate = delegate;
        this.matcher = matcher;
    }

    public void onAuthentication(Authentication authentication,
            HttpServletRequest request, HttpServletResponse response)
            throws SessionAuthenticationException {
        if(matcher.matches(request)) {
            delegate.onAuthentication(authentication, request, response);
        }
    }
}

Then you can inject a RequestMatcher and the ConcurrentSessionControlAuthenticationStrategy into the class. The easiest way to configure this would be to create a BeanPostProcessor:

public class ConcurrentSessionControlAuthenticationStrategyBeanPostProcessor
        implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        if(!(bean instanceof CompositeSessionAuthenticationStrategy)) {
            return bean;
        }

        RequestMatcher matcher = antMatchers("/about", "/","/contact");
        SessionAuthenticationStrategy original = (SessionAuthenticationStrategy) bean;
        return new MatcherSessionAuthenticationStrategy(original, matcher);
    }

    /**
     * Create a {@link List} of {@link AntPathRequestMatcher} instances.
     *
     * @param httpMethod the {@link HttpMethod} to use or {@code null} for any
     * {@link HttpMethod}.
     * @param antPatterns the ant patterns to create {@link AntPathRequestMatcher}
     * from
     *
     * @return an OrRequestMatcher with a {@link List} of {@link AntPathRequestMatcher} instances
     */
    public static RequestMatcher antMatchers(
            String... antPatterns) {
        List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
        for (String pattern : antPatterns) {
            matchers.add(new AntPathRequestMatcher(pattern));
        }
        return new OrRequestMatcher(matchers);
    }
}

You can then add the following to your configuration:

@Bean
public static BeanPostProcessor sessionBeanPostProcessor() {
    return new ConcurrentSessionControlAuthenticationStrategyBeanPostProcessor();
}

It is important to use a static method since this is a BeanPostProcessor which will need to be initialized early on.

PS I'd consider formatting your configuration as outlined in this blog

Vivle answered 17/6, 2015 at 21:24 Comment(1)
This doesn't seem to work. From what I can tell, given his all access area is a permitAll the user will be associated to an AnonymousAuthenticationToken and in SessionManagementFilter the only way to get to the sessionAuthenticationStrategy.onAuthentication call is if the following check is true (authentication != null && !trustResolver.isAnonymous(authentication)). Given the user is anonymous it fails and goes directly to a chunk of code that will likely call invalidSessionStrategy.onInvalidSessionDetected which will do a redirect. Current thought is adding a custom invalidSessionStrategyPrepotency

© 2022 - 2024 — McMap. All rights reserved.