Я пытаюсь использовать Hazelcast Distributed Cache для репликации сеанса HTTP с Spring Boot & Spring Security, но не могу это настроить (однако простая репликация кэша работает нормально, я проверил это, установив какое-то значение в map на одномузел приложения и пытаюсь получить его на другом узле кластера.)Когда приложение запускается в кластере, после входа в систему на одном узле я не получаю объект сеанса на другом узле (я получаю сеанс из объекта реестра сеанса).
Я включил зависимости: версия hazelcast: '3.12'и hazelcast-all версия: '3.12' в файле сборки Gradle.
Ниже приведены конфигурации кода, которые я пробовал до сих пор.
Я добавил ниже bean-компонент в AppConfig.java
@Bean
public Config hazelCastConfig(){
Config config = new Config();
config.setInstanceName("hazelcast-instance")
.addMapConfig(
new MapConfig()
.setName("hazelcastConfiguration")
.setMaxSizeConfig(new MaxSizeConfig(200, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE))
.setEvictionPolicy(EvictionPolicy.LRU)
.setTimeToLiveSeconds(-1));
NetworkConfig networkConfig = config.getNetworkConfig();
networkConfig.setPort(6701).setPortCount(20);
networkConfig.setPortAutoIncrement(true);
JoinConfig join = networkConfig.getJoin();
join.getMulticastConfig().setEnabled(false);
join.getTcpIpConfig()
.addMember("localhost")
.setEnabled(true);
return config;
}
@Bean
public FilterRegistrationBean hazelcastFilter(HazelcastInstance hazelcastInstance) {
FilterRegistrationBean registration = new FilterRegistrationBean(new SpringAwareWebFilter());
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
registration.addUrlPatterns("/*");
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE);
registration.addInitParameter("sticky-session", "false");
registration.addInitParameter("instance-name", hazelcastInstance.getName());
return registration;
}
@Bean
public ServletListenerRegistrationBean<SessionListener> hazelcastSessionListener() {
return new ServletListenerRegistrationBean<SessionListener>(new SessionListener());
}
Из основного класса я исключил SessionAutoConfiguration.class
@SpringBootApplication
(exclude =
{
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
SessionAutoConfiguration.class
}
)
Ниже мой SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* Reference of UserDetailsService service class instance.
* @see UserDetailsService
*/
@Autowired
private UserDetailsService userDetailsService;
/**
* Reference of CustomAuthenticationSuccessHandler instance.
* @see CustomAuthenticationSuccessHandler
*/
@Autowired
private CustomAuthenticationSuccessHandler authenticationSuccessHandler;
/**
* Reference of CustomAuthenticationEntryPoint instance.
* @see CustomAuthenticationEntryPoint
*/
@Autowired
private CustomAuthenticationEntryPoint authenticationEntryPoint;
/**
* Reference of CustomAuthenticationFailureHandler instance.
* @see CustomAuthenticationFailureHandler
*/
@Autowired
private CustomAuthenticationFailureHandler authenticationFailureHandler;
/**
* Reference of CustomLogoutSuccessHandler instance.
* @see CustomLogoutSuccessHandler
*/
@Autowired
CustomLogoutSuccessHandler customLogoutSuccessHandler;
/**
* Reference of PasswordEncoder utility instance.
* @see PasswordEncoder
*/
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private SessionRegistry sessionRegistry;
/**
* Method representing security configuration details, provides AuthenticationManager.
*
* @param auth Allows for easily building in memory authentication, LDAP authentication, JDBC based
* authentication, adding {@link UserDetailsService}, and adding
* AuthenticationProviders.
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
/**
* Method returning a bean of AuthenticationManager which is available during application lifecycle.
*
* @return an instance of default AuthenticationManager.
* @throws Exception
*/
@Bean
public AuthenticationManager customAuthenticationManager() throws Exception {
return authenticationManager();
}
/**
* Method returning a bean of {@link ServletContextInitializer} to register {@link EventListener}s in a Servlet
* 3.0+ container.
*
* This bean can be used to register the following types of listener:
* <ul>
* <li>{@link ServletContextAttributeListener}</li>
* <li>{@link ServletRequestListener}</li>
* <li>{@link ServletRequestAttributeListener}</li>
* <li>{@link HttpSessionAttributeListener}</li>
* <li>{@link HttpSessionListener}</li>
* <li>{@link ServletContextListener}</li>
* </ul>
*
* @return ServletListenerRegistrationBean
*/
@Bean
public static ServletListenerRegistrationBean httpSessionEventPublisher() {
return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
}
/**
* Method returning a bean of custom authentication filter containing custom success and failure handlers.
* Also sets SessionAuthenticationStrategy in filter.
*
* @return CustomUsernamePasswordAuthenticationFilter
* @see CustomUsernamePasswordAuthenticationFilter
* @throws Exception
*/
@Bean
public CustomUsernamePasswordAuthenticationFilter authenticationFilter() throws Exception {
CustomUsernamePasswordAuthenticationFilter authenticationFilter
= new CustomUsernamePasswordAuthenticationFilter(sessionRegistry);
authenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
authenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);
authenticationFilter.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/api/login", "POST"));
authenticationFilter.setAuthenticationManager(customAuthenticationManager());
authenticationFilter.setSessionAuthenticationStrategy(concurrentSession());
return authenticationFilter;
}
/**
* Method representing configuration/strategy for concurrent sessions.
*
* @return CompositeSessionAuthenticationStrategy A SessionAuthenticationStrategy that accepts multiple
* SessionAuthenticationStrategy implementations to delegate to. Each
* SessionAuthenticationStrategy is invoked in turn.
*/
@Bean
public CompositeSessionAuthenticationStrategy concurrentSession() {
ConcurrentSessionControlAuthenticationStrategy concurrentAuthenticationStrategy =
new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry);
concurrentAuthenticationStrategy.setMaximumSessions(1);
concurrentAuthenticationStrategy.setExceptionIfMaximumExceeded(false);
List<SessionAuthenticationStrategy> delegateStrategies = new ArrayList<>();
delegateStrategies.add(concurrentAuthenticationStrategy);
delegateStrategies.add(new SessionFixationProtectionStrategy());
delegateStrategies.add(new RegisterSessionAuthenticationStrategy(sessionRegistry));
CompositeSessionAuthenticationStrategy authenticationStrategy =
new CompositeSessionAuthenticationStrategy(delegateStrategies);
return authenticationStrategy;
}
/**
* Method returning a bean of ConcurrentSessionFilter which is available during application life-cycle.
*
* @return ConcurrentSessionFilter
*/
@Bean
ConcurrentSessionFilter concurrentSessionFilter() {
CustomConcurrentSessionFilter concurrentSessionFilter = new CustomConcurrentSessionFilter(sessionRegistry);
return concurrentSessionFilter;
}
/**
* Method representing different types of security rules/configuration for the application.
*
* @param http HttpSecurity object to configure HTTP security parameters.
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.sessionManagement().sessionAuthenticationStrategy(concurrentSession());
http.addFilterBefore(concurrentSessionFilter(), ConcurrentSessionFilter.class);
http.authorizeRequests()
.antMatchers("/api/secure/org/**",
"/v2/api-docs",
"/configuration/ui",
"/swagger-resources",
"/configuration/security",
"/swagger-ui.html",
"/webjars*//**//**",
"/swagger-resources/configuration/ui").
hasAnyAuthority("ADMIN").anyRequest().fullyAuthenticated()
.antMatchers("/api/secure/dms/**").
hasAnyAuthority("ADMIN","INTERNAL").anyRequest().fullyAuthenticated()
.antMatchers("/api/secure/ext/**","/api/secure/tests/**").
hasAnyAuthority("ADMIN","INTERNAL","EXT").anyRequest().fullyAuthenticated()
.and()
.addFilterBefore(
authenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new RequestFilter(), BasicAuthenticationFilter.class)
/*.addFilterBefore(new RequestFilter(), BasicAuthenticationFilter.class)
.formLogin().loginPage("/api/login")
.permitAll()
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.usernameParameter("email")
.passwordParameter("password")
.and()
.httpBasic().and()*/
.csrf().ignoringAntMatchers("/api/login","/api/auth/**","/api/secure/**")
.csrfTokenRepository(csrfTokenRepository())
.and()
.logout().logoutUrl("/api/logout")
.invalidateHttpSession(false).logoutSuccessHandler(customLogoutSuccessHandler)
.permitAll();
// http.logout().
// logoutUrl("/api/auth/logout").
// logoutSuccessHandler(customLogoutSuccessHandler);
//http.csrf().disable();
}
/**
* Method overriding/representing security configuration/rules to bypasses configured URLs.
*
* @param web WebSecurity object to apply rules.
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/auth/**","/api/application/**","/api/unsecure/**");
}
/**
* This method configure global security.
*
* @param auth AuthenticationManagerBuilder object
* @throws Exception
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
/**
* This method sets CSRF header name in CSRF token repository.
*
* @return CsrfTokenRepository repository object
*/
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
Может кто-нибудь подсказать, какой набор конфигурации мне не хватаетили, может быть, кто-то может поделиться примером кода или любым ресурсом, с которым я могу правильно его настроить.
Требуется просто реплицировать сеансы, чтобы другие узлы кластера знали о существующих сеансах.
Спасибозаранее !!!