2017-01-12 7 views
2

私は、Springのセキュリティを認証と承認に使用しているSpring-MVCアプリケーションに取り組んでいます。私たちがスレッドを使用したい場合、Javaスレッドを使用しているときはいつでも、認証されたユーザーの詳細を取得しようとするいくつかのタスクがあります。Javaスレッドでは春のセキュリティセッションが利用できません

私も@Asyncアノテーションを試しましたが、スレッドで必要とするコードの特定の部分だけを見ているわけではありませんでした。

Javaスレッドでspring-sessionを挿入する方法はありますか?そうでない場合は、別のスレッドでコードの一部を実行する別の方法です。ありがとうございました。

例コード:今

public void sendNotification(Notification notification, int memberId) { 
      Thread thread = new Thread(() -> { 
      Person onlinePerson = this.personService.getCurrentlyAuthenticatedUser(); 
      GroupMembers groupMembers = this.groupMembersService.getMemberById(memberId); 
} 
thread.start(); 
} 

、上記のコードで、私はそのようなonlinePerson.getIdとIDなどonlinePerson、(に関する情報を取得しようとした場合);,私はNPEを得ます。エラーログを確認してください:

Exception in thread "Thread-9" java.lang.NullPointerException 
    at com.project.spring.chat.ChatServiceImpl.lambda$sendNotification$3(ChatServiceImpl.java:532) 
    at java.lang.Thread.run(Thread.java:745) 

私はこの問題にどのように対処できますか。ありがとうございました。

セキュリティapplicationContext.xmlを:

<security:http pattern="/resources/**" security="none"/> 
    <security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true"> 
     <security:form-login login-page="/login" username-parameter="j_username" password-parameter="j_password" 
          login-processing-url="/j_spring_security_check" default-target-url="/canvaslisting" 
          always-use-default-target="true" authentication-failure-url="/denied"/> 
     <security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService" 
           token-validity-seconds="1209600" data-source-ref="dataSource"/> 
     <security:logout delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout"/> 
     <!--<security:intercept-url pattern="/**" requires-channel="https"/>--> 
     <security:port-mappings> 
      <security:port-mapping http="80" https="443"/> 
     </security:port-mappings> 
     <security:logout logout-url="/logout" logout-success-url="/" success-handler-ref="myLogoutHandler"/> 

     <security:session-management session-fixation-protection="newSession"> 
      <security:concurrency-control session-registry-ref="sessionReg" max-sessions="5" expired-url="/login"/> 
     </security:session-management> 
    </security:http> 

    <beans:bean id="sessionReg" class="org.springframework.security.core.session.SessionRegistryImpl"/> 

    <beans:bean id="rememberMeAuthenticationProvider" 
       class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices"> 
     <beans:constructor-arg index="0" value="_spring_security_remember_me"/> 
     <beans:constructor-arg index="1" ref="userDetailsService"/> 
     <beans:constructor-arg index="2" ref="jdbcTokenRepository"/> 
     <property name="alwaysRemember" value="true"/> 
    </beans:bean> 

    <beans:bean id="jdbcTokenRepository" 
       class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl"> 
     <beans:property name="createTableOnStartup" value="false"/> 
     <beans:property name="dataSource" ref="dataSource"/> 
    </beans:bean> 

    <!-- Remember me ends here --> 
    <security:authentication-manager alias="authenticationManager"> 
     <security:authentication-provider user-service-ref="LoginServiceImpl"> 
      <security:password-encoder ref="encoder"/> 
     </security:authentication-provider> 
    </security:authentication-manager> 

    <beans:bean id="encoder" 
       class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"> 
     <beans:constructor-arg name="strength" value="11"/> 
    </beans:bean> 

    <beans:bean id="daoAuthenticationProvider" 
       class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"> 
     <beans:property name="userDetailsService" ref="LoginServiceImpl"/> 
     <beans:property name="passwordEncoder" ref="encoder"/> 
    </beans:bean> 
</beans> 
+1

「DelegatingSecurityContextExecutor」を使用して、SecurityContextを新しいスレッドに渡すことができます。 [Spring Security 3.2 M1のハイライト、Servlet 3 APIのサポート](http://spring.io/blog/2012/12/17/spring-security-3-2-m1-highlights-servlet-3-api-support/)を参照してください。 )。 'AsyncConfigurer'の実装でそれを設定して、' @ Async'でも動作するようにすることもできます。 – Ritesh

答えて

2

これは、セッション情報がHTTP要求を処理したスレッドに対してThreadLocalに格納されているためです。デフォルトでは、新しいスレッド(手動で作成する場合でも@Asyncから作成する場合もあります)では使用できません。

幸い、これは簡単に設定することができます:

SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL) //in a context loader listener for example

または環境変数を持つ:

spring.security.strategy=MODE_INHERITABLETHREADLOCAL

+0

を送信していただきありがとうございます。 Security-applicationContext.xmlで質問を更新しました。上記の設定がどこにあるのか知っていますか? –

+0

私はそこに行けるとは思わない。プログラム的に設定する最初の方法や起動時のJVMオプションとして 'ContextLoaderListener'が必要です。' -Dspring.security.strategy = MODE_INHERITABLETHREADLOCAL' – StuPointerException

+0

これはうまくいきました。ご覧のとおり、別のユーザーとこのことについても議論しています。私は、スレッドが実行すべきコードを提供するためにRunnableとよく混同されていますが、Runnable自体はスレッドとは関係ありません。これは単なるrun()メソッドのオブジェクトです。これは、作成している新しいThread()がスレッドではないことを意味しますか?ありがとうございました。 –

0
public class HelloThread extends Thread { 

    public void run() { 
     System.out.println("Hello from a thread!"); 
     sendNotification(notification, memberId); 
    } 

    public static void main(String args[]) { 
     (new HelloThread()).start(); 
    } 

} 

Runnableは、多くの場合、そのスレッドがrun必要がありますが、Runnable自体はスレッドとは何の関係もありませんコードを提供するために使用されます。それはちょうどrun()メソッドを持つオブジェクトです。

+0

私はあなたのポイントを理解していません。あなたは詳細を教えていただけますか?ありがとうございました。 –

0
public void sendNotification(Notification notification, int memberId) { 
     Thread thread = new Thread(new Runnable(){ 
      public void run() 
      { 
       Person onlinePerson = this.personService.getCurrentlyAuthenticatedUser(); 
       GroupMembers groupMembers = this.groupMembersService.getMemberById(memberId); 
      } 
     } 
     thread.start(); 
    } 
+0

これは、新しいThread()で作成しているスレッドが、本当にスレッドではないことを意味しますか? –

+0

はい、あなたはスレッドを作成しておらず、新しいObject()で同期して、 – gygy19

-1

はい、あなたがスレッドを作成し、あなたの要求のために持っていけないがstatic final new Object()で同期して送信します

+0

私はdownvoteを削除することができますので、答えを編集してください –

1

あなたのスレッドクラスはあなたのコード例では、コンストラクタを呼び出してクラスインスタンスを手動で作成するためです。スレッドインスタンスをSpringで管理し、SpringのApplicationContextを使用してBeanから取得する必要があります。私は、複数のスレッドインスタンスを同時に実行したいと思いますか?その場合は、スレッドの実行ごとに新しいスレッドインスタンス(Bean)が必要になることがあります。

@Component 
@Scope("prototype") // the scope “prototype" will return a new instance each time when the bean will be retrieved. 
public class YourThreadClass implements Runnable { 
    private String anyString; 

    @Autowired 
    AnyOtherBean anyOtherBean; // since YourThreadClass is now managed by spring, you can also inject services/components into YourThreadClass 
} 

@Component 
public class YourClassThatStartsTheThread{ 
    @Autowired 
    ApplicationContext ctx; 

    public void sendNotification(Notification notification, int memberId){ 
     // ... your code .... 
     yourThreadClass aNewInstanceOfYourThreadClass = (YourThreadClass) ctx.getBean(yourThreadClass); 
     // ... your code ... 
    } 
} 
関連する問題