2015-12-14 54 views
1

我有问题,有时得到AuthenticationCredentialsNotFoundException。目前我认为这是一个线程问题。根据另一个问题(linkSecurityContext通过不同线程之间的对象HttpSession - 但由于某种原因,这不适合我。为什么SecurityContext的Authentication对象不能在线程间共享?

这是怎么了处理登录的那一刻:

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

这是的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 --> 
<!-- //////////////////////////////////////////////////////////////////////////////// --> 

什么失败是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(); 

Wh因为SecurityContextHolder.getContext().getAuthentication()null,所以它叫credentialsNotFound


服务器开机后出现故障的堆栈跟踪从第一登录比较:

[http-bio-8080-exec-4] DEBUG com.mz.server.servlet.LoginServletImpl - Login request by userId: [email protected] 
[http-bio-8080-exec-4] DEBUG com.mz.server.service.LoginService - Login request for [email protected] 
[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: [email protected] 
[http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): [email protected] 
[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 [email protected] 
[http-bio-8080-exec-4] INFO com.mz.server.repository.jooq.shop.ShopAdminRepository - Checking password for [email protected] 
[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 [email protected]: Username: [email protected]; 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 [[email protected]] 
[http-bio-8080-exec-4] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): [email protected] 
[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: [email protected]9bee56: Authentication: [email protected] 
[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) 
.... 

到工作堆栈跟踪:

[http-bio-8080-exec-7] DEBUG com.mz.server.servlet.LoginServletImpl - Login request by userId: [email protected] 
[http-bio-8080-exec-7] DEBUG com.mz.server.service.LoginService - Login request for [email protected] 
[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: [email protected] 
[http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): [email protected] 
[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 [email protected] 
[http-bio-8080-exec-7] INFO com.mz.server.repository.jooq.shop.ShopAdminRepository - Checking password for [email protected] 
[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 [email protected]: Username: [email protected]; 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 [[email protected]] 
[http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - findByUserEmail(): [email protected] 
[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: [email protected]a22883: Authentication: [email protected] 
[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: [email protected] 
[http-bio-8080-exec-7] DEBUG org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframewor[email protected]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 - [email protected] 
[http-bio-8080-exec-7] DEBUG com.mz.server.repository.jooq.shop.ShopAdminRepository - Fetching shops for shop_admin_id 1 

我们可以看到差异是第一个stacktrace是由两个线程[http-bio-8080-exec-4][http-bio-8080-exec-6]。我经常看到这种行为,即线程名称发生更改,然后出现此异常。因此,这似乎是一个多线程的问题


这是整个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 (e.g. 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> 
+0

你知道吗,不同的请求,试图在第一时间访问什么资源?你如何提供静态内容?有可能是其中一个线程试图从登录页面访问一些受保护的资源(image/css)? –

+0

@StefanHaberl那么,您看到日志的请求来自点击登录按钮。这不仅仅是查看数据库并检查给定用户名的密码是否正确。请参阅'用户成功验证[[email protected]]'条目,它告诉我登录实际上正在工作。所以不,在这个行动中没有额外的资源。 – displayname

回答

0

嗯,我有这样的:

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

,并简单地将其更改为:

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

我也在我的applicationContext弹簧取下

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

。XML和我说这个别名

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

最后的applicationContext-spring.xml看起来是这样的:

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

我把this作为参考。

0

你必须allowSessionCreation设置为false

请注意,将此标志设置为false并不会阻止此类存储安全上下文。如果您的应用程序(或其他过滤器)创建了一个会话,那么安全上下文仍将存储为已验证的用户。

您是否正确创建会话?或者你真的想要安全性而不是创建会话?

+0

嗨!哇,有趣的是:D我觉得我刚刚从一篇教程中获得了这个。我*想*我想让Spring Security创建会话,我猜。我想登录一个用户,并让它在会话期间发送请求。为什么有人不想开会?我不确定自己是否正确创建了会话,但我会尝试但我会尽快将'allowSessionCreation'设置为'true',并让您知道这是否正常工作。 – displayname

+0

我试着将它设置为“true”,但它仍然是一样的。一个不同的线程进来时,我得到了异常。 – displayname

+0

你可以发布任何其他相关的配置吗?例如,web.xml – denov

0

,你可以从一个线程转移SecurityContext的另一个

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

new Thread(wrappedRunnable).start(); 

见并发支持

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

相关问题