Spring Boot - Hazelcast Session Replication with Spring Security - spring-boot

I am trying to use Hazelcast Distributed Cache for replication of HTTP session with Spring Boot & Spring Security but unable to set this up (however, simple cache replication is working fine, I have verified this by setting some value in map on one application node and trying to get it on other cluster node).
I have gone through the stuff available over web but unfortunately I am unable to set this up. Application when runs in a cluster, after logging on one node, I do not get session object on other node (I am fetching session from session registry object).
I have included dependencies : hazelcast version: '3.12' and hazelcast-all version: '3.12'
in gradle build file.
Below is the code configurations I have tried so far.
I have added below bean in 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());
}
From Main Class, I have excluded SessionAutoConfiguration.class
#SpringBootApplication
(exclude =
{
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
SessionAutoConfiguration.class
}
)
Below is my 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;
}
}
Can someone please suggest what set of configuration I am missing, or maybe if someone could share sample code or any resource with which I can configure it correctly.
Requirement is just to replicate sessions so that other cluster nodes are aware of existing sessions.
Thanks in advance!!!

Please check my sample project here: https://github.com/gokhanoner/seajug-demo
It uses Hazelcast as session cache as well using Spring Session, I believe it's what you needed & it's a simpler setup.

Related

antMatchers & permitAll doesn't let me visit the REST point

The following line doesn't let me visit GET: /api/topics without a bearer token. It works if I apply a token. Am I missing something? Isn't permitAll supposed to do that?
.antMatchers("/api/topics/**").permitAll()
By the way, I tried with /api/topics** and it didn't work as well.
Error:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
Result without a token (the broken part). I want it to let me through.
Result with a token. It works as intended:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/topics/**").permitAll()
.antMatchers("/api/users/**").permitAll()
.anyRequest().authenticated();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
#RestController
#RequestMapping("/api/topics")
public class TopicController {
#Autowired
private TopicService topicService;
#Autowired
private UserService userService;
#Autowired
private TopicMapper topicMapper;
/**
* Gets all topics.
*
* #return the topics.
*/
#GetMapping
public ResponseEntity<List<TopicDTO>> getAll() {
return ResponseEntity.ok(topicMapper.toTopicDTOs(topicService.getAll()));
}
/**
* Gets topic by id.
*
* #param id the id.
* #return the topic.
*/
#GetMapping("/{id}")
public ResponseEntity<TopicDTO> get(#PathVariable("id") Long id) {
Optional<TopicEntity> topicEntity = topicService.get(id);
return topicEntity.map(entity -> ResponseEntity.ok(topicMapper.toTopicDTO(entity))).orElseGet(() -> ResponseEntity.notFound().build());
}
/**
* Creates a new topic.
*
* #param topicDTO the topic DTO.
* #return the new topic DTO.
*/
#PostMapping
public ResponseEntity<TopicDTO> create(#RequestBody TopicDTO topicDTO) {
UserEntity userEntity = userService.get(topicDTO.getUserId()).orElseThrow(() -> new IllegalArgumentException("User does not exist."));
TopicEntity topicEntity = topicMapper.toTopicEntity(topicDTO);
topicEntity.setId(null);
topicEntity.setUser(userEntity);
Optional<TopicEntity> createdTopicEntity = topicService.create(topicEntity);
return createdTopicEntity.map(entity -> ResponseEntity.ok(topicMapper.toTopicDTO(entity))).orElseGet(() -> ResponseEntity.status(HttpStatus.CONFLICT).build());
}
/**
* Updates an existing topic.
* #param id the topic id.
* #param topicDTO the topic DTO.
* #return the updated topic DTO.
*/
#PutMapping("/{id}")
public ResponseEntity<TopicDTO> update(#PathVariable("id") Long id, #RequestBody TopicDTO topicDTO) {
UserEntity userEntity = userService.get(topicDTO.getUserId()).orElseThrow(() -> new IllegalArgumentException("User does not exist."));
TopicEntity topicEntity = topicMapper.toTopicEntity(topicDTO);
topicEntity.setId(id);
topicEntity.setUser(userEntity);
Optional<TopicEntity> updatedTopicEntity = topicService.update(topicEntity);
return updatedTopicEntity.map(entity -> ResponseEntity.ok(topicMapper.toTopicDTO(entity))).orElseGet(() -> ResponseEntity.badRequest().build());
}
/**
* Deletes an existing topic.
* #param id the topic id.
* #return the status code.
*/
#DeleteMapping("/{id}")
public ResponseEntity<Void> delete(#PathVariable("id") Long id) {
if (topicService.get(id).isPresent()) {
topicService.delete(id);
return ResponseEntity.ok().build();
}
return ResponseEntity.notFound().build();
}
}
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private TokenStore tokenStore;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("trusted")
.secret(bCryptPasswordEncoder.encode("secret"))
.authorizedGrantTypes("password", "get_token", "refresh_token")
.scopes("read", "write")
.autoApprove(true)
.accessTokenValiditySeconds(15 * 60)
.refreshTokenValiditySeconds(30 * 60);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.tokenStore(tokenStore);
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration {
}
Because #EnableResourceServer will add its filter chain at order=3 by default.As for WebSecurityConfigurerAdapter implementation, adds its own filterchain at the order=100, as a result, request first goes through filter chain of set by #EnableResourceServer where everything is protected unless you provide token and that's why you are getting that behavior. Try to add order bellow 3 like #Order(2) annotation to your WebSecurityConfigurerAdapter implementation.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled = true)
#Order(2) <<--- add this
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
For more information read: Changing the Filter Order
There is a mistake with URL patterns in antMatchers.
By default the pattern like /api/topics/** doesn't match /api/topics.
It matches only /api/topics/ and after slash can be zero or more symbols
For fixing this case can be several solutions:
Change the pattern in existing antMatchers to next /api/topics**
Use mvcMatchers instead of antMatchers. mvcMatchers("/api/topics").permitAll()
mvcMatchers - will use the same rules that Spring MVC uses for
matching. For example, often times a mapping of the path "/path" will
match on "/path", "/path/", "/path.html", etc.
More information about antMatchers can be found here
More information about mvcMatchers can be found here

AbstractMongoEventListener is not getting invoked

I have the following class
class MongoCascadeSaveEventListener extends AbstractMongoEventListener<Object> {
#Override
public void onBeforeConvert(final BeforeConvertEvent<Object> event) {
}
}
Bean definition
#Bean
public MongoCascadeSaveEventListener mongoCascadeSaveEventListener() {
return new MongoCascadeSaveEventListener();
}
onBeforeConvert is never being called.
When i check the class of MongoTemplate the event publisher is set like following
eventPublisher = new MongoMappingEventPublisher(indexCreator);
The class from spring mongo package. When i see the class i dont think the implementation is correct and that explains why listener is not invoked.
public class MongoMappingEventPublisher implements ApplicationEventPublisher {
private final MongoPersistentEntityIndexCreator indexCreator;
/**
* Creates a new {#link MongoMappingEventPublisher} for the given {#link MongoPersistentEntityIndexCreator}.
*
* #param indexCreator must not be {#literal null}.
*/
public MongoMappingEventPublisher(MongoPersistentEntityIndexCreator indexCreator) {
Assert.notNull(indexCreator, "MongoPersistentEntityIndexCreator must not be null!");
this.indexCreator = indexCreator;
}
/*
* (non-Javadoc)
* #see org.springframework.context.ApplicationEventPublisher#publishEvent(org.springframework.context.ApplicationEvent)
*/
#SuppressWarnings("unchecked")
public void publishEvent(ApplicationEvent event) {
if (event instanceof MappingContextEvent) {
indexCreator.onApplicationEvent((MappingContextEvent<MongoPersistentEntity<?>, MongoPersistentProperty>) event);
}
}
/*
* (non-Javadoc)
* #see org.springframework.context.ApplicationEventPublisher#publishEvent(java.lang.Object)
*/
public void publishEvent(Object event) {}
}
is this a bug or am i missing something here ? Using 2.0.5.Release version of spring data mongo.
Your config looks fine and the listener should be called, MongoTemplate implements ApplicationContextAware and hence after construction it sets eventPublisher to the applicationContext

Websocket Stomp - Broadcast(Topic,queue)

How do I broadcast to all subscribers(Topic) and the specified user(Channel).
this.messagingTemplate.convertAndSend(destination, message);
this.messagingTemplate.convertAndSendToUser(userId, destination, message);
Is that correct?
What is WebSocketConnectHandlerDecoratorFactory class for?
public final class WebSocketConnectHandlerDecoratorFactory implements WebSocketHandlerDecoratorFactory {
private static final Log logger = LogFactory.getLog(WebSocketConnectHandlerDecoratorFactory.class);
private final ApplicationEventPublisher eventPublisher;
/**
* Creates a new instance
*
* #param eventPublisher the {#link ApplicationEventPublisher} to use. Cannot be null.
*/
public WebSocketConnectHandlerDecoratorFactory(ApplicationEventPublisher eventPublisher) {
Assert.notNull(eventPublisher, "eventPublisher cannot be null");
this.eventPublisher = eventPublisher;
}
#Override
public WebSocketHandler decorate(WebSocketHandler handler) {
return new SessionWebSocketHandler(handler);
}
private final class SessionWebSocketHandler extends WebSocketHandlerDecorator {
public SessionWebSocketHandler(WebSocketHandler delegate) {
super(delegate);
}
#Override
public void afterConnectionEstablished(WebSocketSession wsSession)
throws Exception {
super.afterConnectionEstablished(wsSession);
publishEvent(new SessionConnectEvent(this,wsSession));
}
private void publishEvent(ApplicationEvent event) {
try {
eventPublisher.publishEvent(event);
}
catch (Throwable ex) {
logger.error("Error publishing " + event + ".", ex);
}
}
}
}
Correct.
See its JavaDocs:
/**
* Ensures that a {#link SessionConnectEvent} is published in
* {#link WebSocketHandler#afterConnectionEstablished(WebSocketSession)}. This
* is necessary so that the {#link WebSocketSession} can be mapped to the
* corresponding Spring {#link Session} to terminate any
* {#link WebSocketSession} associated with a Spring {#link Session} that was
* destroyed.
*
* #author Rob Winch
* #since 1.0
*
* #see WebSocketRegistryListener
*/

Configuring Spring Security with Spring Boot

I am new to configuring Spring Security using Java Config. I was trying to follow this posting. However, when I run my app, I get a Basic Auth challenge on all URLs, including /. Entering the either of the userid/pass combos below do not seem to work.
My Controller:
package com.xxx.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
#RequestMapping("/")
/**
* Controller to handle basic "root" URLs
*
* #author xxx
* #version 0.1.0
*/
public class RootController {
/**
* Handles '/'
* #param model
* #return
*/
#RequestMapping
public String index(Model model) {
return "index";
}
/**
* Handles '/signup'
* #param model
* #return
*/
#RequestMapping("/signup")
public String signup(Model model) {
return "signup";
}
/**
* Handles '/about'
* #param model
* #return
*/
#RequestMapping("/about")
public String about(Model model) {
return "about";
}
/**
* Handles '/login'
* #param model
* #return
*/
#RequestMapping("/login")
public String login(Model model) {
return "login";
}
/**
* Handles '/admin'
* #param model
* #return
*/
#RequestMapping("/admin")
public String admin(Model model) {
return "admin";
}
}
Not sure what else to try. Just looking for some guidance as to why this isn't working.
Update
For completeness, here is the config class:
package com.xxx.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
/**
* Configures the security for the application
*
* #author XXX
* #version 0.1.0
*
*/
public class WebSecurityAppConfig extends WebSecurityConfigurerAdapter {
#Override
/**
* #see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#registerAuthentication(AuthenticationManagerBuilder)
*/
protected void registerAuthentication(AuthenticationManagerBuilder auth)
throws Exception {
auth
.inMemoryAuthentication()
.withUser("user") // #1
.password("password")
.roles("USER")
.and()
.withUser("admin") // #2
.password("password")
.roles("ADMIN","USER");
}
#Override
/**
* #see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(WebSecurity)
*/
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**"); // #3
}
#Override
/**
* #see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(HttpSecurity)
*/
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/signup","/about").permitAll() // #4
.antMatchers("/admin/**").hasRole("ADMIN") // #6
.anyRequest().authenticated() // 7
.and()
.formLogin() // #8
.loginPage("/login") // #9
.permitAll(); // #5
}
}
And the WebApplicationInitializer:
package com.xxx.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
*
* #author XXX
* #version 0.1.0
*/
public class SpringWebMvcInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
/**
*
*/
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebSecurityAppConfig.class };
}
#Override
/**
*
*/
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
/**
*
*/
protected String[] getServletMappings() {
return null;
}
}
I didn't include these before because they are pretty much a copy-paste from the referenced blog posting.
The original question is probably best answered by just describing the options available. Some applications (services that only need basic HTTP authentication) can use the default settings in the actuator, others will need to specify security.* properties (see SecurityProperties for options) and/or an AuthenticationManager (for user account details). The next level of control comes from adding your own WebSecurityConfigurerAdapter, and you can see how to do that by looking at the "secure" sample in Spring Boot.

ContextLoaderListener Configuration with AbstractAnnotationConfigDispatcherServletInitialier not loaded

I use Spring version 3.2.2.RELEASE.
I want to create a webapp with hibernate and have the folllowing configuration:
public class LifepulseServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { PersistenceConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { LifepulseWebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected WebApplicationContext createRootApplicationContext() {
return super.createRootApplicationContext();
}
}
My two with #Configuration annotated files look like this:
My PersistenceConfig should be used for the ContextLoaderListener:
#Configuration
#EnableTransactionManagement(proxyTargetClass=true, mode=AdviceMode.PROXY)
public class PersistenceConfig{
Logger logger = LoggerFactory.getLogger(PersistenceConfig.class);
/** The Application Context. */
#Autowired
ApplicationContext context;
/**
* Gets the database url.
*
* #return the database url
*/
#Bean(name="databaseUrl")
public String getDatabaseUrl(){
return "jdbc:mysql://localhost/lifepulse";
}
/**
* Gets the session factory properties.
*
* #return the session factory properties
*/
#Bean(name="sessionFactoryProperties")
public Properties getSessionFactoryProperties(){
Properties props = new Properties();
props.put("hibernate.dialect", MySQL5InnoDBDialect.class.getName());
props.put("hibernate.hbm2ddl.auto", "create-drop");
props.put("hibernate.show_sql", "true");
props.put("hibernate.format_sql", "true");
return props;
}
/** The Constant ANNOTATED_CLASSES. */
#SuppressWarnings("unchecked")
private static final Class<? extends Serializable>[] ANNOTATED_CLASSES=new Class[]{
Melder.class,
LogEntry.class
};
/**
* Gets the annotated classes.
*
* #return the annotated classes
*/
private static Class<? extends Serializable>[] getAnnotatedClasses(){
return ANNOTATED_CLASSES;
}
/**
* Gets the data source.
* This bean represents the application's MYSQL datasource, without using xml.
*
* #return the data source
*/
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl(getDatabaseUrl());
dataSource.setUsername("lifepulse");
dataSource.setPassword("lifepulse");
return dataSource;
}
/**
* Session factory.
* This bean represents the Hibernate Session Factory. By declaring this bean
* it can easily be injected into Spring DAOs later on.
*
* #return the local session factory bean
*/
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
factory.setAnnotatedClasses(getAnnotatedClasses());
factory.setHibernateProperties(getSessionFactoryProperties());
factory.setDataSource(dataSource());
return factory;
}
#Bean()
public GenericDao genericDao() {
return new HibernateDaoImpl();
}
#Bean
PlatformTransactionManager txManager(){
HibernateTransactionManager htm = new HibernateTransactionManager(sessionFactory().getObject());
return htm;
}
}
My LifepulseWebConfig should be used for the DispatcherSerlvet:
#Configuration
#EnableWebMvc
#ComponentScan(value= {"com.ansiworks.lifepulse.controllers"})
#EnableTransactionManagement(proxyTargetClass=true, mode=AdviceMode.PROXY)
public class LifepulseWebConfig extends WebMvcConfigurerAdapter{
#Autowired
ApplicationContext context;
#Bean
ViewResolver viewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
//resolver.setPrefix("");
resolver.setSuffix(".jsp");
return resolver;
}
#Bean
MainService mainService(){
return new MainServiceImpl();
}
}
This configuration doesn't work. The dispathcer servlet doesn't load my PersistenceConfig, and so the beans are not herited to the LifepulseWebConfig.
But: When I add the PersistenceConfig to the class-array of the Method LifepulseServletInitializer#getServletConfigClasses() everything works fine.
However... I can't use sprin-security in this way....
What am I doing wrong!? Why are the config-classes returned by LifepulseServletInitializer#getRootConfigClasses() not read by the ContextLoaderListener?
I also tried it this way (without the LifepulseServletInitializer ) with the same effect. The beans of my PersistenceConfig are simply not loaded...
public class MyWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(PersistenceConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(LifepulseWebConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}

Resources