0

私はしばらくこの問題で立ち往生しており、あなたの助けが必要です. 私はできるだけ多くの詳細を提供しようとし、必要に応じて編集します.

Spring セキュリティからカスタム UserDetailsS​​ervice でデータベースにユーザーを作成しようとしています。シナリオは次のとおりです。ユーザーがアプリケーションにログインすると、ユーザーが存在するかどうかデータベースを確認します。そうでない場合は、Web サービスを呼び出してこのユーザーが存在するかどうかを確認し、すべての情報をインポートして自分のデータベースに保存し、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)

アプリケーションの他の部分 (認証後) では、コントローラーから任意のサービスを使用できます。挿入+選択は正常に機能します。

私の構成については、次のファイルがあります。春のセキュリティ構成 (Spring セキュリティ コンテキスト) は "applicationContext-security.xml" にあります。Spring Bean (ルート コンテキスト) は "applicationContext.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>

私のトランザクションマネージャは mode="aspectj" です (プロキシなしで試しました) すべての Aspectj ライブラリが完全に動作しています。読み込み時の織り込みなど。また、アプリケーションで *.aj ファイルも使用しています。また、customUserDetailsS​​ervice クラスに aop:advisor を追加しようとしました

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 には、コントローラーの context:component-scan と、webapp にのみ必要なその他のものがあります。と<aop:aspectj-autoproxy/>他の aop:advisors。

Eclipse STS に埋め込まれた ApacheTomcat Service でテストしています

編集:完全なスタックトレース:

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

1 に答える 1

1

私はついに解決策を見つけました...それはばかげた間違いでしたが、私は解決策に近かった. 上記の構成は実際には優れています。RootContext と SecurityContext は、トランザクションと Bean を共有しています。

トランザクションを開始せずにエンティティマネージャーから persist() メソッドを手動で呼び出すと、別のスタックトレースが得られました。問題は実際には JSR303 検証の失敗でした... (Web サービスからの不正な RegExp)

したがって、将来の使用のために、「dataSource」をSpring SecurityContextと共有したい場合、この構成(質問)が機能します。私の問題からスタックトレースを得るために、私は以下を使用しました:

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

とにかく助けてくれてありがとう:)

于 2013-03-22T14:49:48.210 に答える