Spring-请求来自不同主机时的引导 2.0 会话问题

Spring-boot 2.0 session issue when request is coming from different host

所以我已经将我的应用程序从 spring-boot 1.5 更新到 spring-boot 2.0。当请求来自任何主机时,我能够使用 spring-boot 1.5 版本正确登录,但现在 Spring boot 2.0 的问题是只有来自同一主机的请求在工作,但请求来自不同的主机他们的会话正在改变。下面是我在 spring-boot 2.0 中的 spring 安全配置,这是导致问题的原因。

 package com.s4m.digiid.config;

    import java.util.Arrays;
    import java.util.List;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.env.Environment;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
    import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
    import org.springframework.session.FindByIndexNameSessionRepository;
    import org.springframework.session.Session;
    import org.springframework.session.security.SpringSessionBackedSessionRegistry;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.CorsConfigurationSource;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

    import com.s4m.digiid.handler.CustomAccessDeniedHandler;
    import com.s4m.digiid.handler.CustomAuthFailureHandler;
    import com.s4m.digiid.handler.LoginHandler;
    import com.s4m.digiid.handler.LogoutHandler;
    import com.s4m.digiid.service.impl.AuthenticationService;
    import com.s4m.digiid.util.ApiConstants;

    @EnableGlobalMethodSecurity(prePostEnabled = true)
    @EnableWebSecurity
    @EnableJpaRepositories(basePackages = "com.s4m.digiid.repository")
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        private Environment env;        

        @Autowired
        private LogoutHandler logoutHandler;

        @Autowired
        private LoginHandler loginHandler;

        @Autowired
        private CustomAuthFailureHandler customAuthFailure;

        @Autowired
        private CustomAccessDeniedHandler accessDeniedHandler;

        @Autowired
        private AuthenticationService authService;


        @Value("${maximum.sessions}")
        private Integer maxSessions;

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(authService)
            .passwordEncoder(passwordEncoder());
        }


        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }

        @Autowired
        private FindByIndexNameSessionRepository<? extends Session> sessionRepository;


        @Bean
        public SpringSessionBackedSessionRegistry sessionRegistry() {
            return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
        }

        @Bean
        public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
            return new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .antMatchers(ApiConstants.SWAGGER_DOC_API, ApiConstants.SWAGGER_CONFIG_UI, ApiConstants.SWAGGER_RESOURCES,
                    ApiConstants.SWAGGER_CONFIG, ApiConstants.SWAGGER_HTML_UI, ApiConstants.WEB_JARS, ApiConstants.FAVICON).permitAll()
                .and().formLogin().successHandler(loginHandler).failureHandler(customAuthFailure)
                .and().logout()    
                .logoutUrl("/appLogout") 
                .logoutSuccessHandler(logoutHandler)
                .and().exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler)
                .and().csrf().disable()
                .cors().and()
                .addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .sessionManagement().maximumSessions(maxSessions).sessionRegistry(sessionRegistry()).maxSessionsPreventsLogin(false);
        }


        @Bean
        public AuthenticationFilter authenticationFilter() throws Exception {
            AuthenticationFilter filter = new AuthenticationFilter();
            filter.setAuthenticationManager(authenticationManagerBean());
            filter.setAuthenticationFailureHandler(customAuthFailure);
            filter.setAuthenticationSuccessHandler(loginHandler);
            return filter;
        }


        @Bean
        public CorsConfigurationSource corsConfigurationSource()
        {
            List<String> allowedClients = Arrays.asList(env.getProperty("digiid.allowed.clients").split(","));
             CorsConfiguration configuration = new CorsConfiguration();
             configuration.setAllowedOrigins(allowedClients);
             configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS"));
             configuration.setMaxAge(Long.parseLong("3600"));
             configuration.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization,X-Auth-Token"));
             configuration.setAllowCredentials(true);
             UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
             source.registerCorsConfiguration("/**", configuration);
             return source;
        }

    }

下面是 spring-boot 1.5 中的代码,运行良好。

package com.s4m.digiid.config;

import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import com.s4m.digiid.handler.CustomAccessDeniedHandler;
import com.s4m.digiid.handler.CustomAuthFailureHandler;
import com.s4m.digiid.handler.LoginHandler;
import com.s4m.digiid.handler.LogoutHandler;
import com.s4m.digiid.service.impl.AuthenticationService;
import com.s4m.digiid.util.ApiConstants;

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@EnableRedisHttpSession
@EnableJpaRepositories(basePackages = "com.s4m.digiid.repository")
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private Environment env;        

    @Autowired
    private LogoutHandler logoutHandler;

    @Autowired
    private LoginHandler loginHandler;

    @Autowired
    private CustomAuthFailureHandler customAuthFailure;

    @Autowired
    private CustomAccessDeniedHandler accessDeniedHandler;

    @Autowired
    private AuthenticationService authService;

    @Autowired 
    private SpringSessionBackedSessionRegistry sessionRegistry;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(authService)
        .passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers(ApiConstants.SWAGGER_DOC_API, ApiConstants.SWAGGER_CONFIG_UI, ApiConstants.SWAGGER_RESOURCES,
                ApiConstants.SWAGGER_CONFIG, ApiConstants.SWAGGER_HTML_UI, ApiConstants.WEB_JARS, ApiConstants.FAVICON).permitAll()
            .and().formLogin().successHandler(loginHandler).failureHandler(customAuthFailure)
            .and().logout()    
            .logoutUrl("/appLogout") 
            .logoutSuccessHandler(logoutHandler)
            .and().exceptionHandling()
            .accessDeniedHandler(accessDeniedHandler)
            .and().csrf().disable()
            .cors().and()
            .addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
            .sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry).maxSessionsPreventsLogin(false);        
    }

    @Bean
    public AuthenticationFilter authenticationFilter() throws Exception {
        AuthenticationFilter filter = new AuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.setAuthenticationFailureHandler(customAuthFailure);
        filter.setAuthenticationSuccessHandler(loginHandler);
        return filter;
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource()
    {
        List<String> allowedClients = Arrays.asList(env.getProperty("digiid.allowed.clients").split(","));
         CorsConfiguration configuration = new CorsConfiguration();
         configuration.setAllowedOrigins(allowedClients);
         configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS"));
         configuration.setMaxAge(Long.parseLong("3600"));
         configuration.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization,X-Auth-Token"));
         configuration.setAllowCredentials(true);
         UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
         source.registerCorsConfiguration("/**", configuration);
         return source;
    }

}

随着 spring-boot 升级到 2.x,在 DefaultCookieSerializer.java class 中处理的 Cookie 的“SameSite”属性默认设置为“Lax”。

Try setting  "SameSite" attribute's value to "None".

更多详情,https://datatracker.ietf.org/doc/html/draft-west-first-party-cookies-07