spring security - expiredUrl not working

2020-05-19 07:12发布

问题:

I need to configure expired-url in my Spring MVC application. Here is my effort, but has no effect:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .addFilterBefore(adminAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        .addFilterBefore(customerAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        .csrf()
            .disable()
        .authorizeRequests()
            .antMatchers("...", "...", "...").permitAll()
            .anyRequest().authenticated()
        .and()
            .formLogin()
                .loginPage("/admin/login")
        .and()
            .logout()
                .addLogoutHandler(customLogoutHandler())
                .logoutSuccessHandler(customLogoutSuccessHandler())
                .logoutUrl("/logout")
        .deleteCookies("remove")
        .invalidateHttpSession(true)
            .permitAll()
        .and()
        .sessionManagement()
            .maximumSessions(1)
            .expiredUrl("/expired");

}

This does not have any effect and when the user's session times out, spring does not redirect him to /expired url and just redirects him to /admin/login url.

Update:

I tried suggested solutions in the comments and answer, but did not see any effect. Also I removed addLogoutHandler(), logoutSuccessHandler() and two addFilterBefore() at beginning of method, but not working.

Also I tried another solution in this way:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .addFilterBefore(sessionManagementFilter(), SessionManagementFilter.class)
        .csrf()
            .disable()
        .authorizeRequests()
            .antMatchers("...", "...", "...").permitAll()
            .anyRequest().authenticated()
        .and()
            .formLogin()
                .loginPage("/admin/login")
        .and()
            .logout()
                .logoutUrl("/logout")
        .deleteCookies("remove")
        .invalidateHttpSession(true)
            .permitAll();
}

@Bean
public SessionManagementFilter sessionManagementFilter() {
    SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(httpSessionSecurityContextRepository());
    sessionManagementFilter.setInvalidSessionStrategy(simpleRedirectInvalidSessionStrategy());
    return sessionManagementFilter;
}

@Bean
public SimpleRedirectInvalidSessionStrategy simpleRedirectInvalidSessionStrategy() {
    SimpleRedirectInvalidSessionStrategy simpleRedirectInvalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy("/expired");
    return simpleRedirectInvalidSessionStrategy;
}

@Bean
public HttpSessionSecurityContextRepository httpSessionSecurityContextRepository(){
    HttpSessionSecurityContextRepository httpSessionSecurityContextRepository = new HttpSessionSecurityContextRepository();
    return httpSessionSecurityContextRepository;
}

Could anyone help me to solve this problem?

回答1:

ConcurrentSessionFilter will redirect to expiredUrl, if the valid session ID is marked as expired in SessionRegistry, see Spring Security reference:

- expired-url The URL a user will be redirected to if they attempt to use a session which has been "expired" by the concurrent session controller because the user has exceeded the number of allowed sessions and has logged in again elsewhere. Should be set unless exception-if-maximum-exceeded is set. If no value is supplied, an expiry message will just be written directly back to the response.

SessionManagementFilter will redirect to invalidSessionUrl, if the session ID is not valid (timeout or wrong ID), see Spring Security reference:

If the user is not currently authenticated, the filter will check whether an invalid session ID has been requested (because of a timeout, for example) and will invoke the configured InvalidSessionStrategy, if one is set. The most common behaviour is just to redirect to a fixed URL and this is encapsulated in the standard implementation SimpleRedirectInvalidSessionStrategy. The latter is also used when configuring an invalid session URL through the namespace,as described earlier.

Both URLs (expiredUrl, invalidSessionUrl) have to be configured as permitAll().

BTW: If you want to use Concurrent Session Control with maximumSessions you have to add HttpSessionEventPublisher to your web.xml:

Concurrent Session Control

If you wish to place constraints on a single user’s ability to log in to your application, Spring Security supports this out of the box with the following simple additions. First you need to add the following listener to your web.xml file to keep Spring Security updated about session lifecycle events:

<listener>
    <listener-class>
           org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>


回答2:

I tried the Ali Dehghani's solution(in the comments) in this way:

.sessionManagement().maximumSessions(1).and().invalidSessionUrl("/expired");

And as The Coder said, add "/expired" in the permitted urls and the problem solved. Thank you everybody who has paid attention to my problem, especially Ali Dehghani and The Coder, for their useful comments.



回答3:

Ideally your UX should simply redirect your user back to the login page. I guess you see the requirement of having a dedicated /expired page because of Spring MVC - change security settings dynamically where you informed about your need of having separate login masks. If the workaround (the one that I described in my answer on your other question) works for you, you could maybe drop your requirement of having a dedicated /expired page and redirect the user to the correct login page directly using teh solution approach number (2). How about that?

Nevertheless, to answer your current question... I'm not sure if it works but give it a try and change your code

        //...
        .sessionManagement()
        .maximumSessions(1)
        .expiredUrl("/expired");
    }

to

        //...
        .sessionManagement().sessionFixation().newSession().maximumSessions(1)
        .expiredUrl("/expired")
        .sessionRegistry(sessionRegistry());
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        SessionRegistry sessionRegistry = new SessionRegistryImpl();
        return sessionRegistry;
    }

In case it doesn't work, could you then post the code of your customLogoutHandler() and customLogoutSuccessHandler()? You are using Spring MVC outside of Spring Boot, correct?



回答4:

If you use UserDetails and UserDetailsService then it should be because your UserDetails implementation class there is no Override hashCode () and equals (Object obj) method. This is my implementation class for UserDetails:

public class MyUser implements UserDetails {
    private String username;

    private String password;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return null;
    }

    @Override
    public String getUsername() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return false;
    }

    @Override
    public int hashCode() {
        return username.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return this.toString().equals(obj.toString());
    }
}