2013-03-22 133 views
0

我一直在這個問題上停留了一段時間,我需要你的幫助。我會盡量給予儘可能多的細節,並根據需要進行編輯。在UserDetailsS​​ervice實現中拋出RollbackException,無法提交JPA事務(休眠)

我想從Spring安全性的自定義UserDetailsS​​ervice數據庫中創建用戶。 這種情況是:當用戶登錄我的應用程序時,如果用戶存在,我檢查我的數據庫;如果沒有,我打電話給一個webservice來檢查這個用戶是否存在,並導入他的所有信息,以便將它們保存到我自己的數據庫中,並授予他訪問Web應用程序的權限。

我可以從數據庫中讀取信息(SELECT查詢),但沒有得到以下(美麗的)錯誤,我不能做任何插入:

org.springframework.security.authentication.AuthenticationServiceException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction 
org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:85) 

在應用程序(一次鑑別的)的另一部分,我可以使用控制器的任何服務。插入+選擇正常工作。

關於我的配置,我有以下文件: 春季安全配置(春季安全上下文)在 「的applicationContext-security.xml文件」 中的 「applicationContext.xml中」 Spring MVC的 春豆(根上下文)(DispatcherServlet的)在webmvc-config.xml中 和的applicationContext-jpa.xml +的persistence.xml

執行的UserDetailsS​​ervice的:

@Transactional(propagation = Propagation.REQUIRES_NEW) 
public class CustomUserDetailsService implements UserDetailsService { 


    @PersistenceContext 
    private EntityManager em; 

    [...Other Declarations...] 

    @Autowired 
    private userService userService; 

    @Override 
    public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException { 
     User user = null; //extends spring security UserDetails 

      //If I try to find existing users in database here, it works perfectly 
     User user = userService.findUserByLogin(login); 

     List<GrantedAuthority> authorities = ... 
     Set<String> permissions = ... 

     if(user == null){ 
      //Call webservice and get User if exists => newUser 

      //Save in user table 
       userService.saveUser(newUser); 
       //No error here, I can even try a userService.findUser(newUser.getId()); and I get the results. the commit is after returning the UserDetails from this method. 


      //I also tried the following solution... the entitymanager is well Autowired and I can access everything as usual 
      // em.getTransaction().begin(); // => Throw here 'nested exception is java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead' 
      // em.persist(operator); 
      // em.getTransaction().commit(); 
      // em.close(); 
        user = newUser 

      } 
      else{ 
         //Other things 
      } 

     } 
     return user; //if I tried a saveUser, commit is done after the return and the exception is thrown 
    } 
} 

我試圖上的類和方法本身@Transactional的所有組合。

現在所有的配置文件[只有配置文件的有用部分]

的web.xml:

<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value> 
    </context-param> 
    <listener> 
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> 
    </listener> 
    <listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
    </listener> 
    <servlet> 
    <servlet-name>myApp</servlet-name> 
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
    <init-param> 
     <param-name>contextConfigLocation</param-name> 
     <param-value>WEB-INF/spring/webmvc-config.xml</param-value> 
    </init-param> 
    <load-on-startup>1</load-on-startup> 
    </servlet> 

的applicationContext.xml:

<context:spring-configured/> 

<context:component-scan base-package="com.company.myApp"> 
    <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/> 
</context:component-scan> 


<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource"> 
    <property name="driverClassName" value="${database.driverClassName}"/> 
    <property name="url" value="${database.url}"/> 
    <property name="username" value="${database.username}"/> 
    <property name="password" value="${database.password}"/> 
    <property name="testOnBorrow" value="true"/> 
    <property name="testOnReturn" value="true"/> 
    <property name="testWhileIdle" value="true"/> 
    <property name="timeBetweenEvictionRunsMillis" value="1800000"/> 
    <property name="numTestsPerEvictionRun" value="3"/> 
    <property name="minEvictableIdleTimeMillis" value="1800000"/> 
    <property name="validationQuery" value="SELECT 1 FROM DUAL"/> 
</bean> 

<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory"/> 
</bean> 

<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/> 

<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory"> 
    <property name="persistenceUnitName" value="persistenceUnit"/> 
    <property name="dataSource" ref="dataSource"/> 
</bean> 

我的交易經理是在mod e =「aspectj」(我沒有它,代理嘗試) 我有所有Aspectj庫完美工作。加載時間編織和什麼。 我也在我的應用程序中使用* .aj文件。 我也嘗試添加一個AOP:導師對我的customUserDetailsS​​ervice類

的applicationContext-security.xml文件:

<beans:beans xmlns="http://www.springframework.org/schema/security" 
    xmlns:beans="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
     http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd" profile="local,dev,int"> 
    <global-method-security pre-post-annotations="enabled"> 
     <expression-handler ref="expressionHandler"/> 
    </global-method-security> 
    <!-- HTTP security configurations --> 
    <http auto-config="true" use-expressions="true"> 
     <expression-handler ref="webExpressionHandler"/> 
     <form-login login-processing-url="/resources/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t" /> 
     <logout logout-url="/resources/j_spring_security_logout" /> 
     <!-- Configure these elements to secure URIs in your application --> 
     <intercept-url pattern="/resources/**" access="permitAll" /> 
     <intercept-url pattern="/login/**" access="permitAll" /> 
     <intercept-url pattern="/**" access="isAuthenticated()" /> 
     <session-management session-fixation-protection="migrateSession"> 
      <concurrency-control max-sessions="1"/> 
     </session-management> 
     <x509 subject-principal-regex="CN=[^,]* ([^,]*),.*$" user-service-ref="customUserDetailsService" /> 
     <logout delete-cookies="JSESSIONID" /> 
    </http> 
    <!-- Configure Authentication mechanism --> 
    <authentication-manager alias="authenticationManager"> 
     <!-- SHA-256 values can be produced using 'echo -n your_desired_password | sha256sum' (using normal *nix environments) --> 
     <authentication-provider user-service-ref="customUserDetailsService"> 
      <password-encoder hash="sha-256" /> 
     </authentication-provider> 
    </authentication-manager>  
</beans:beans> 

ApplicationContext的-jpa.xml只有基本的倉庫聲明:

<beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/data/jpa" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> 

     <repositories base-package="com.company.myApp.common.repository" /> 

</beans> 

webmvc-config具有上下文:用於控制器的組件掃描以及僅用於Web應用程序的其他內容。 與<aop:aspectj-autoproxy/>和其他aop:顧問。

我測試嵌入在Eclipse的STS

編輯ApacheTomcat服務:全堆棧跟蹤:

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction 
org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:85) 
org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:132) 
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156) 
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) 
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94) 
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:194) 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323) 
org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doFilter(AbstractPreAuthenticatedProcessingFilter.java:88) 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323) 
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105) 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323) 
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323) 
org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125) 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323) 
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:173) 
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) 
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259) 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) 
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) 
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) 
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) 
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:615) 
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) 
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293) 
org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859) 
org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:602) 
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:409) 
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 
java.lang.Thread.run(Thread.java:619) 
+0

你能提供完整的堆棧跟蹤?我在想,在引起回滾的線路上會出現異常。 – CodeChimp 2013-03-22 11:10:20

+0

我加了stacktrace,謝謝你的幫助。我檢查了調試模式,並沒有任何明顯的錯誤(try/catch);它堅持在數據庫(沒有提交),並在我可以選擇它之後。代碼會遍歷整個方法併發送返回值。當我硬編碼'newUser'來繞過服務調用(saveUser())時,它可以工作。 – Rigg802 2013-03-22 12:48:33

回答

1

我終於找到了解決辦法...這是一個愚蠢的錯誤,但我很接近解決方案。上面的配置其實很好。 RootContext和SecurityContext共享事務和bean。

通過從實體管理器手動調用persist()方法而不開始事務,給了我另一個堆棧跟蹤。這個問題實際上是一個JSR303驗證失敗...(來自web服務的錯誤RegExp)

因此,爲了將來的使用,這個配置(在問題中)的作品,如果你想共享'dataSource'到Spring SecurityContext 。 有從我的問題,我用下面的堆棧跟蹤:

em.persist(user); 
    em.flush(); 
    em.close(); 

謝謝你的幫助呢:)