Why is the SecurityContext authentication object not used in threads?

I have a problem getting an AuthenticationCredentialsNotFoundException . At the moment, I believe this is a threading issue. According to another question ( link ), the SecurityContext is passed through the HttpSession object between different threads, but for some reason this does not work for me.

This is how I handle login at the moment:

 public ShopAdminDTO login(String userEmail, String password) throws EmailAddressNotFoundException { LOGGER.debug("Login request for " + userEmail); // Create and initialize user details object for Spring Security authentication mechanism. ShopAdminUserDetails userDetails = new ShopAdminUserDetails(userEmail, password, true, true, true, true, new ArrayList<GrantedAuthority>()); // Create authentication object for the Spring SecurityContext Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, password, new ArrayList<GrantedAuthority>()); boolean requiresEmailActivation = this.shopAdminValidationTokenRepository.getRequiresEmailValidation(userEmail); if(requiresEmailActivation == true) { LOGGER.info("Login denied: Email is not validated yet."); // IMPORTANT NOTE: We throw an EmailNotFoundException instead of a // PleaseValidateYourEmailFirstException in order to NOT reveal // that this email exists. So: Do not "FIX" this! throw new EmailAddressNotFoundException(); } LOGGER.debug("Email appears validated."); try { // Execute authentication chain to try user authentication auth = this.adminAuthenticationProvider.authenticate(auth); } catch(BadCredentialsException e) { // FIXME Login: We could/should count and limit login attempts here? LOGGER.info("Bad credentials found for: " + userEmail); throw e; } LOGGER.info("User successfully authenticated [userEmail="+userEmail+"]"); // Set the authentication to the SecurityContext, the user is now logged in SecurityContext securityContext = SecurityContextHolder.getContext(); securityContext.setAuthentication(auth); // Finally load the user data ShopAdminDTO shopAdminDto = this.shopAdminRepository.findByUserEmail(userEmail); return shopAdminDto; } 

This is applicationContext-security.xml

 <!-- //////////////////////////////////////////////////////////////////////////////// --> <!-- // BEGIN Spring Security --> <sec:http pattern="/**" auto-config="true" use-expressions="true"/> <bean id="httpSessionSecurityContextRepository" class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'> <property name='allowSessionCreation' value='false' /> </bean> <bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"> <constructor-arg ref="httpSessionSecurityContextRepository" /> </bean> <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy"> <constructor-arg> <list> <sec:filter-chain pattern="/**" filters="securityContextPersistenceFilter" /> </list> </constructor-arg> </bean> <bean id="authenticationListener" class="com.mz.server.web.auth.CustomAuthenticationListener"/> <bean id="authenticationProvider" class="com.mz.server.web.auth.CustomAuthenticationProvider"/> <bean id="userDetailsService" class="com.mz.server.web.service.CustomUserDetailsService"/> <sec:authentication-manager alias="authenticationManager"> <sec:authentication-provider ref="authenticationProvider"/> </sec:authentication-manager> <bean id="permissionEvaluator" class="com.mz.server.web.auth.permission.CustomPermissionEvaluator"> <constructor-arg index="0"> <map key-type="java.lang.String" value-type="com.mz.server.web.auth.permission.Permission"> <entry key="isTest" value-ref="testPermission"/> </map> </constructor-arg> </bean> <bean id="testPermission" class="com.mz.server.web.auth.permission.TestPermission"> </bean> <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"> <property name="permissionEvaluator" ref="permissionEvaluator"/> </bean> <sec:global-method-security authentication-manager-ref="authenticationManager" pre-post-annotations="enabled"> <sec:expression-handler ref="expressionHandler"/> </sec:global-method-security> <!-- // END Spring Security --> <!-- //////////////////////////////////////////////////////////////////////////////// --> 

What fails is this part of the AbstractSecurityInterceptor#beforeInvocation :

 if (debug) { logger.debug("Secure object: " + object + "; Attributes: " + attributes); } if (SecurityContextHolder.getContext().getAuthentication() == null) { credentialsNotFound(messages.getMessage( "AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes); } Authentication authenticated = authenticateIfRequired(); 

If it calls credentialsNotFound because SecurityContextHolder.getContext().getAuthentication() is null .


Comparison of failed stack trace from first login after server boot:

 [http-bio-8080-exec-4] DEBUG com.mz.server.servlet.LoginServletImpl - Login request by userId: user@gmx.at [http-bio-8080-exec-4] DEBUG com.mz.server.service.LoginService - Login request for user@gmx.at [http-bio-8080-exec-4] DEBUG com.mz.server.service.LoginService - Email appears validated.. authenticating.. [http-bio-8080-exec-4] INFO com.mz.server.spring.auth.AdminAuthenticationProvider - authenticate(), User email: user@gmx.at [http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): user@gmx.at [http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - User found. [http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Loading password salt for user@gmx.at [http-bio-8080-exec-4] INFO com.mz.server.repository.jooq.shop.ShopAdminRepository - Checking password for user@gmx.at [http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Password valid. [http-bio-8080-exec-4] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - getPrincipal() [http-bio-8080-exec-4] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - Setting user com.mz.server.spring.auth.ShopAdminUserDetails@8ac733b2: Username: user@gmx.at; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities to 'authenticated'. [http-bio-8080-exec-4] INFO com.mz.server.service.LoginService - User successfully authenticated [userEmail=user@gmx.at] [http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): user@gmx.at [http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - User found. [http-bio-8080-exec-6] DEBUG com.mz.server.servlet.shop.ShopServletImpl - Requested available shops. [http-bio-8080-exec-6] INFO com.mz.server.servlet.shop.ShopServletImpl - [http-bio-8080-exec-6] INFO com.mz.server.servlet.shop.ShopServletImpl - [http-bio-8080-exec-6] INFO com.mz.server.servlet.shop.ShopServletImpl - SPRING_SECURITY_CONTEXT: org.springframework.security.core.context.SecurityContextImpl@259bee56: Authentication: com.mz.server.spring.auth.CustomUserAuthentication@259bee56 [http-bio-8080-exec-6] INFO com.mz.server.servlet.shop.ShopServletImpl - [http-bio-8080-exec-6] INFO com.mz.server.servlet.shop.ShopServletImpl - [http-bio-8080-exec-6] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.util.List com.mz.server.service.ShopService.getAvailableShops(); target is of class [com.mz.server.service.ShopService]; Attributes: [[authorize: 'isAuthenticated()', filter: 'null', filterTarget: 'null']] [http-bio-8080-exec-6] DEBUG com.mz.server.spring.auth.CustomHttpSessionListener - AuthenticationCredentialsNotFoundEvent Jun 09, 2016 8:06:42 PM org.apache.catalina.core.ApplicationContext log SEVERE: Exception while dispatching incoming RPC call com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract java.util.List com.mz.shared.web.service.shop.ShopServlet.getAvailableShops()' threw an unexpected exception: org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext at com.google.gwt.user.server.rpc.RPC.encodeResponseForFailure(RPC.java:416) at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:605) .... 

to the stack trace:

 [http-bio-8080-exec-7] DEBUG com.mz.server.servlet.LoginServletImpl - Login request by userId: user@gmx.at [http-bio-8080-exec-7] DEBUG com.mz.server.service.LoginService - Login request for user@gmx.at [http-bio-8080-exec-7] DEBUG com.mz.server.service.LoginService - Email appears validated.. authenticating.. [http-bio-8080-exec-7] INFO com.mz.server.spring.auth.AdminAuthenticationProvider - authenticate(), User email: user@gmx.at [http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): user@gmx.at [http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - User found. [http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Loading password salt for user@gmx.at [http-bio-8080-exec-7] INFO com.mz.server.repository.jooq.shop.ShopAdminRepository - Checking password for user@gmx.at [http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Password valid. [http-bio-8080-exec-7] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - getPrincipal() [http-bio-8080-exec-7] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - Setting user com.mz.server.spring.auth.ShopAdminUserDetails@8ac733b2: Username: user@gmx.at; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities to 'authenticated'. [http-bio-8080-exec-7] INFO com.mz.server.service.LoginService - User successfully authenticated [userEmail=user@gmx.at] [http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): user@gmx.at [http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - User found. [http-bio-8080-exec-7] DEBUG com.mz.server.servlet.shop.ShopServletImpl - Requested available shops. [http-bio-8080-exec-7] INFO com.mz.server.servlet.shop.ShopServletImpl - [http-bio-8080-exec-7] INFO com.mz.server.servlet.shop.ShopServletImpl - [http-bio-8080-exec-7] INFO com.mz.server.servlet.shop.ShopServletImpl - SPRING_SECURITY_CONTEXT: org.springframework.security.core.context.SecurityContextImpl@1ea22883: Authentication: com.mz.server.spring.auth.CustomUserAuthentication@1ea22883 [http-bio-8080-exec-7] INFO com.mz.server.servlet.shop.ShopServletImpl - [http-bio-8080-exec-7] INFO com.mz.server.servlet.shop.ShopServletImpl - [http-bio-8080-exec-7] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.util.List com.mz.server.service.ShopService.getAvailableShops(); target is of class [com.mz.server.service.ShopService]; Attributes: [[authorize: 'isAuthenticated()', filter: 'null', filterTarget: 'null']] [http-bio-8080-exec-7] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - isAuthenticate(): true [http-bio-8080-exec-7] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Previously Authenticated: com.mz.server.spring.auth.CustomUserAuthentication@1ea22883 [http-bio-8080-exec-7] DEBUG org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter@653fccd, returned: 1 [http-bio-8080-exec-7] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Authorization successful [http-bio-8080-exec-7] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - RunAsManager did not change Authentication object [http-bio-8080-exec-7] DEBUG com.mz.server.service.ShopService - Getting available shops for .. [http-bio-8080-exec-7] DEBUG com.mz.server.spring.auth.CustomUserAuthentication - getPrincipal() [http-bio-8080-exec-7] DEBUG com.mz.server.service.ShopService - user@gmx.at [http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Fetching shops for shop_admin_id 1 

We see that the difference is that the first stacktrace is created by two streams [http-bio-8080-exec-4] and [http-bio-8080-exec-6] . I often see this behavior, that the name of the thread changes, and this exception occurs. So this is a multithreading issue.


This is the whole web.xml :

 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>mz | life</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <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> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> </listener> <listener> <listener-class>com.mz.server.BootstrappingServerConfig</listener-class> </listener> <!-- --> <servlet> <servlet-name>application</servlet-name> <servlet-class>com.mz.server.servlet.app.ApplicationDataServletImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>application</servlet-name> <url-pattern>/app/application</url-pattern> </servlet-mapping> <!-- --> <servlet> <servlet-name>login</servlet-name> <servlet-class>com.mz.server.servlet.LoginServletImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/app/login</url-pattern> </servlet-mapping> <!-- --> <servlet> <servlet-name>shop</servlet-name> <servlet-class>com.mz.server.servlet.shop.ShopServletImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>shop</servlet-name> <url-pattern>/app/shop</url-pattern> </servlet-mapping> <!-- --> <servlet> <servlet-name>shopadmin</servlet-name> <servlet-class>com.mz.server.servlet.shop.ShopAdminServletImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>shopadmin</servlet-name> <url-pattern>/app/shopadmin</url-pattern> </servlet-mapping> <!-- XSRF-Token Servlet --> <servlet> <servlet-name>xsrf</servlet-name> <servlet-class>com.google.gwt.user.server.rpc.XsrfTokenServiceServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>xsrf</servlet-name> <url-pattern>/app/xsrf</url-pattern> </servlet-mapping> <!-- This is the name of the session cookie set by the Servlet container (eg Tomcat or Jetty) --> <context-param> <param-name>gwt.xsrf.session_cookie_name</param-name> <param-value>JSESSIONID</param-value> </context-param> <!-- --> <servlet> <servlet-name>mobile-restapi</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mobile-restapi</servlet-name> <url-pattern>/app/restapi/*</url-pattern> </servlet-mapping> <!-- --> <servlet> <servlet-name>web-restapi</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/context/applicationContext-restapi.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>web-restapi</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/classes/context/root-context.xml </param-value> </context-param> </web-app> 
+1
spring multithreading spring-security
source share
3 answers

Ok, I have this:

 <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> 

and just changed it to the following:

 <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> 

I also deleted

 <sec:http pattern="/**" auto-config="true" use-expressions="true" /> 

in my application Context- spring.xml and I added this alias

 <alias name="filterChainProxy" alias="springSecurityFilterChain"/> 

The final applicationContext- spring.xml is as follows:

 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sec="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd" > <!-- Imports --> <import resource="applicationContext-spring-acl.xml"/> <bean id="authenticationListener" class="com.mahlzeit.server.spring.auth.CustomAuthenticationListener"/> <bean id="httpSessionListener" class="com.mahlzeit.server.spring.auth.CustomHttpSessionListener"/> <bean id="adminAuthenticationProvider" class="com.mahlzeit.server.spring.auth.AdminAuthenticationProvider"> <constructor-arg ref="dslContext" /> </bean> <bean id="userDetailsService" class="com.mahlzeit.server.service.CustomUserDetailsService"/> <sec:authentication-manager alias="authenticationManager"> <sec:authentication-provider ref="adminAuthenticationProvider"/> </sec:authentication-manager> <!-- Filter Chain --> <bean id="httpSessionSecurityContextRepository" class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'/> <bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"> <constructor-arg ref="httpSessionSecurityContextRepository" /> </bean> <alias name="filterChainProxy" alias="springSecurityFilterChain"/> <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy"> <constructor-arg> <list> <sec:filter-chain pattern="/**" filters="securityContextPersistenceFilter" /> </list> </constructor-arg> </bean> </beans> 

I took this as a reference.

0
source share

You have allowSessionCreation set to false .

Note that setting this flag to false prevents this class from maintaining a security context. If your application (or another filter) creates a session, then the security context will still be stored for the authenticated user.

Are you creating the session correctly? or do you really want security not to create sessions?

0
source share

you can pass SecurityContext from one thread to another

 Runnable originalRunnable = new Runnable() { public void run() { // invoke secured service } }; SecurityContext context = SecurityContextHolder.getContext(); DelegatingSecurityContextRunnable wrappedRunnable = new DelegatingSecurityContextRunnable(originalRunnable, context); new Thread(wrappedRunnable).start(); 

See Concurrency Support

http://docs.spring.io/spring-security/site/docs/current/reference/html/concurrency.html

0
source share

All Articles