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
所以我已经将我的应用程序从 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