6

スプリング フィルターを介してスプリング セキュリティ コンテキストを設定する Web アプリケーションがあります。サービスは、ユーザーの役割に基づくスプリング アノテーションで保護されます。これは機能します。

非同期タスクは JMS リスナーで実行されます (javax.jms.MessageListener を拡張)。このリスナーのセットアップは Spring で行われます。

メッセージは Web アプリケーションから送信され、この時点でユーザーは認証されます。メッセージ処理中に、JMS スレッド (ユーザーとロール) で同じ認証が必要です。

現在、これは Spring 認証を JMS ObjectMessage に入れることによって行われます。

SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();
... put the auth object in jms message object

次に、JMS リスナー内で認証オブジェクトが抽出され、コンテキストに設定されます。

SecurityContext context = new SecurityContextImpl();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);

これはほとんどの場合機能します。ただし、メッセージの処理前に遅延がある場合、メッセージは決して処理されません。これらのメッセージ損失の原因をまだ特定できませんでしたが、メッセージが別のサーバーで処理されるときにカスターで機能したとしても、認証を伝達する方法が適切かどうかはわかりません。

これは春の認証を伝播する正しい方法ですか?

よろしく、ミカエル

4

3 に答える 3

2

より良い解決策は見つかりませんでしたが、これは私にとってはうまくいきます。

JMS メッセージを送信することでAuthenticationヘッダーとして保存し、それぞれ受信してセキュリティ コンテキストを再作成します。Authenticationヘッダーとして保存するには、次のようにシリアル化する必要がありBase64ます。

class AuthenticationSerializer {

 static String serialize(Authentication authentication) {
    byte[] bytes = SerializationUtils.serialize(authentication);
    return DatatypeConverter.printBase64Binary(bytes);
 }

 static Authentication deserialize(String authentication) {
    byte[] decoded = DatatypeConverter.parseBase64Binary(authentication);
    Authentication auth = (Authentication) SerializationUtils.deserialize(decoded);
    return auth;
  }
}

設定した Message ヘッダーを送信するだけで、Message Template 用の Decorator を作成でき、自動的に行われます。デコレータでは、そのようなメソッドを呼び出すだけです:

private void attachAuthenticationContext(Message message){
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    String serialized = AuthenticationSerializer.serialize(auth);
    message.setStringProperty("authcontext", serialized);
}

受信は複雑になりますが、自動で行うこともできます。適用する代わりに、@EnableJMS次の構成を使用します。

@Configuration
class JmsBootstrapConfiguration {

    @Bean(name = JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public JmsListenerAnnotationBeanPostProcessor jmsListenerAnnotationProcessor() {
        return new JmsListenerPostProcessor();
    }

    @Bean(name = JmsListenerConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)
    public JmsListenerEndpointRegistry defaultJmsListenerEndpointRegistry() {
        return new JmsListenerEndpointRegistry();
    }
}

class JmsListenerPostProcessor extends JmsListenerAnnotationBeanPostProcessor {


    @Override
    protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
        return new ListenerEndpoint();
    }

}

class ListenerEndpoint extends MethodJmsListenerEndpoint {
    @Override
    protected MessagingMessageListenerAdapter createMessageListenerInstance() {
        return new ListenerAdapter();
    }
}

class ListenerAdapter extends MessagingMessageListenerAdapter {

    @Override
    public void onMessage(Message jmsMessage, Session session) throws JMSException {
        propagateSecurityContext(jmsMessage);
        super.onMessage(jmsMessage, session);
    }

    private void propagateSecurityContext(Message jmsMessage) throws JMSException {
        String authStr = jmsMessage.getStringProperty("authcontext");        
        Authentication auth = AuthenticationSerializer.deserialize(authStr);
        SecurityContextHolder.getContext().setAuthentication(auth);
    }     

}
于 2016-04-15T13:28:46.450 に答える