I'm trying to develop an application with spring security login functionality while user session is distributed using apache Ignite.
I have two errors in my application.
12-Aug-2017 14:09:01.580 SEVERE [http-nio-8080-exec-2] org.apache.ignite.logger.java.JavaLogger.error Failed to update web session: null java.lang.NullPointerException at org.apache.ignite.cache.websession.WebSessionFilter$RequestWrapperV2.getSession(WebSessionFilter.java:1001) at org.apache.ignite.cache.websession.WebSessionFilter.doFilterV2(WebSessionFilter.java:564) at org.apache.ignite.cache.websession.WebSessionFilter.doFilterDispatch(WebSessionFilter.java:407) at org.apache.ignite.cache.websession.WebSessionFilter.doFilter(WebSessionFilter.java:383) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748)
My configuration files are as follows.
Spring context configuration
<beans xmlns.... >
<context:component-scan base-package="test.ignite.spring"/>
<mvc:annotation-driven/>
<context:property-placeholder location="classpath:system.properties" ignore-resource-not-found="true" ignore-unresolvable="true"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:interceptors>
<mvc:interceptor>
<!-- Cache of HTML pages -->
<mvc:mapping path="/**"/>
<bean class="org.springframework.web.servlet.mvc.WebContentInterceptor">
<property name="cacheSeconds" value="0"/>
</bean>
</mvc:interceptor>
</mvc:interceptors>
</beans>
Login Controller
@Controller
public class LoginController {
@RequestMapping(value = {"login", "/"})
public String login() {
try {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
return "home";
}
return "login";
} catch (Exception e) {
return "redirect:/error/500";
}
}
@RequestMapping(value = "/home")
public String home() {
return "home";
}
}
----UPDATED----
Ignite configuration (whole file content)
<beans xmlns.... >
<bean abstract="true" id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
<!-- Set to true to enable distributed class loading for examples, default is false. -->
<property name="peerClassLoadingEnabled" value="true"/>
<property name="cacheConfiguration">
<list>
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="example"/>
<property name="cacheMode" value="PARTITIONED"/>
</bean>
</list>
</property>
<!-- Enable task execution events for examples. -->
<property name="includeEventTypes">
<list>
<!--Task execution events-->
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_STARTED"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_FINISHED"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_FAILED"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_TIMEDOUT"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_SESSION_ATTR_SET"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_TASK_REDUCED"/>
<!--Cache events-->
<util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_PUT"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_READ"/>
<util:constant static-field="org.apache.ignite.events.EventType.EVT_CACHE_OBJECT_REMOVED"/>
</list>
</property>
<!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. -->
<property name="discoverySpi">
<bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
<property name="ipFinder">
<!--
Ignite provides several options for automatic discovery that can be used
instead os static IP based discovery. For information on all options refer
to our documentation: http://apacheignite.readme.io/docs/cluster-config
-->
<!-- Uncomment static IP finder to enable static-based discovery of initial nodes. -->
<!--<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">-->
<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder">
<property name="addresses">
<list>
<!-- In distributed environment, replace with actual host IP address. -->
<value>127.0.0.1:47500..47509</value>
</list>
</property>
</bean>
</property>
</bean>
</property>
</bean>
Spring security configuration
<beans:beans xmlns.... >
<http auto-config="true" create-session="always" use-expressions="true" >
<form-login login-page="/login" default-target-url="/home" authentication-failure-url="/login?error" username-parameter="username" password-parameter="password" always-use-default-target="true"/>
<logout invalidate-session="true" logout-success-url="/login" delete-cookies="JSESSIONID"/>
<session-management session-fixation-protection="newSession" invalid-session-url="/" session-authentication-error-url="/login">
<concurrency-control session-registry-alias="sessionRegistry" max-sessions="10" expired-url="/" error-if-maximum-exceeded="true"/>
</session-management>
<access-denied-handler error-page="/403"/>
</http>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
</authentication-provider>
</authentication-manager>
</beans:beans>
web.xml
<web-app xmlns... >
<listener>
<listener-class>org.apache.ignite.startup.servlet.ServletContextListenerStartup</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<filter>
<filter-name>IgniteWebSessionsFilter</filter-name>
<filter-class>org.apache.ignite.cache.websession.WebSessionFilter</filter-class>
</filter>
<!-- You can also specify a custom URL pattern. -->
<filter-mapping>
<filter-name>IgniteWebSessionsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Specify Ignite configuration (relative to META-INF folder or Ignite_HOME). -->
<context-param>
<param-name>IgniteConfigurationFilePath</param-name>
<param-value>example-ignite.xml</param-value>
</context-param>
<!-- Specify the name of Ignite cache for web sessions. -->
<context-param>
<param-name>IgniteWebSessionsCacheName</param-name>
<param-value>example</param-value>
</context-param>
<!--SERVLETS-->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc-dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:mvc-dispatcher-servlet.xml,
classpath:security-config.xml
</param-value>
</context-param>
</web-app>
I would be very thankful if you could provide any solution / idea to solve problem.
As both of your deployments are on localhost, they're going to share JSESSIONID
cookie. So far so good.
However there seems to be a mismatch between Tomcat, Spring Security and Ignite which causes Spring Security to regard sessions coming from Ignite as invalid when they're anonymous (i.e. not logged on). I don't understand yet how this relates to your case where you're supposed to already be logged on.
You can probably use a workaround: Remove invalid-session-url="/"
from your Spring Security configuration. This will prevent the redirect looping behavior. This will also cause users to be silently logged off instead to being guided to /login
when their cookie expires.
I've deleted my previous answer as it was missing the point.