我一直在这个问题上停留了一段时间,我需要你的帮助。我会尽量给予尽可能多的细节,并根据需要进行编辑。在UserDetailsService实现中抛出RollbackException,无法提交JPA事务(休眠)
我想从Spring安全性的自定义UserDetailsService数据库中创建用户。 这种情况是:当用户登录我的应用程序时,如果用户存在,我检查我的数据库;如果没有,我打电话给一个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
执行的UserDetailsService的:
@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:导师对我的customUserDetailsService类
的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)
你能提供完整的堆栈跟踪?我在想,在引起回滚的线路上会出现异常。 – CodeChimp 2013-03-22 11:10:20
我加了stacktrace,谢谢你的帮助。我检查了调试模式,并没有任何明显的错误(try/catch);它坚持在数据库(没有提交),并在我可以选择它之后。代码会遍历整个方法并发送返回值。当我硬编码'newUser'来绕过服务调用(saveUser())时,它可以工作。 – Rigg802 2013-03-22 12:48:33