2

私は、サーバー送信イベントをブラウザにプッシュするSseEmitterを呼び出すSpring Controllerメソッドを持っています。ブラウザから、SSE URLのEventSourceを作成するためのjavascriptがホームページから実行されます。ホームページ自体には、Spring Securityフォームベースの認証を使用して行われる認証が必要です。そのため、認証に成功すると、ログインページはホームページに送られます。ただし、認証オブジェクトは、サーバー送信イベント方式でnullです。間違ったSpring Securityの設定がこれを引き起こしていると理解していますが、修正方法はわかりません!Springセキュリティ認証オブジェクトがSpring SseEmitterのnullです

以下

私は

@Configuration 
@EnableWebMvcSecurity 
public class UCFSecurityConfig extends WebSecurityConfigurerAdapter{ 

@Autowired 
@Qualifier("epUser") 
private UserDetailsService userDetailsService; 

@Autowired 
private AuthenticationSuccessHandler successHandler; 
.... 
.... 
@Override 
protected void configure(HttpSecurity http) throws Exception { 
    http 
     .authorizeRequests() 
      .antMatchers("/rest/**").authenticated() 
      .anyRequest().authenticated() 
      .and() 
      .formLogin() 
      .loginPage("/login.htm").failureUrl("/error.htm") 
      .loginProcessingUrl("/spring_sec_auth.htm") 
      .usernameParameter("username").passwordParameter("password").permitAll() 
      .successHandler(successHandler) 
      .and() 
      .logout() 
      //.logoutSuccessUrl("/login.htm?logout") 
      .invalidateHttpSession(true) 
      .and().csrf().disable(); 

} 
.... 
.... 
} 

を使用するセキュリティ設定で以下SSE方法はNPEをスローSecurityContextHolder.getContext().getAuthentication()から認証対象を使用する UserActivityServiceuserActivityFeed()を呼び出し!

@Controller 
public class UserActivityController { 

@Autowired 
private UserActivityService userActivityService; 

@RequestMapping("/userActivity") 
public ResponseBodyEmitter activityFeed() { 

    final SseEmitter emitter = new SseEmitter(); 
    ExecutorService service = Executors.newSingleThreadExecutor(); 
    service.execute(() -> { 
     try { 
      emitter.send(userActivityService.userActivityFeed() , MediaType.TEXT_PLAIN); 
      Thread.sleep(UCFConstants.USR_ACTV_REFRESH_PERIOD); 

     } catch (Exception e) { 
      e.printStackTrace(); 
      emitter.completeWithError(e); 
      return; 
     } 
     emitter.complete(); 
    }); 
    service.shutdown(); 
    return emitter; 
    } 
} 

JavascriptのSSEのEventSourceオブジェクトを作成するための

<script type="text/javascript"> 
var source = new EventSource("/MyApp/rest/userActivity"); 
source.onmessage = function(event) { 
        document.getElementById("result").innerHTML += event.data     
        + "<br>"; 
        }; 
    </script> 

私のアプリケーションのweb.xml

<servlet-mapping> 
    <servlet-name>MyApp</servlet-name> 
    <url-pattern>*.htm</url-pattern> 
</servlet-mapping> 
<servlet-mapping> 
    <servlet-name>MyApp</servlet-name> 
    <url-pattern>/rest/*</url-pattern> 
</servlet-mapping> 

答えて

3

SecurityContextHolder.getContext()実装を見てください。フードの下では、現在のセキュリティコンテキストを保持するためにThreadLocalSecurityContextHolderStrategy戦略を使用します。つまり、現在のセキュリティコンテキストは要求スレッドからしか利用できません。 userActivityFeed()は実際に別のスレッドで実行されているので、SecurityContextHolder.getContext().getAuthentication()を呼び出すとNPEを取得しています。

この作業をすべて行うには、以前のコントローラメソッドで現在のユーザーを取得してuserActivityFeed()に渡すことができます。

+0

'SecurityContextHolder.getContext()はセッション内のリクエスト間で共有されませんか?私は、春のセキュリティ関連オブジェクト(認証、プリンシパルなど)がその後の制限付きURLへのリクエストで利用できない場合、単一のエントリポイント認証(例えばログインURL)のポイントを理解できません – wildthing81

+0

春のドキュメント、https: /docs.spring.io/spring-security/site/docs/3.0.x/reference/technical-overview.html単一のセッションで同時要求を受け取るアプリケーションでは、同じSecurityContextインスタンスがスレッド間で共有されます。 ThreadLocalが使用されていても、各スレッドのHttpSessionから取得されるのは同じインスタンスです。これは、スレッドが実行されているコンテキストを一時的に変更したい場合に意味があります。 – wildthing81

+0

docは実際に要求を処理するコンテナスレッドについて述べています。しかし、あなたの 'userActivityFeed()'メソッドは、それ自身のスレッドでexecutorサービスによって実行されています。このスレッドはSecurityContextについて何も知らない – Leffchik

関連する問題