2013-09-24 11 views
6

Ho un'applicazione web che imposta un contesto di sicurezza a molla attraverso un filtro a molla. I servizi sono protetti con annotazioni a molla in base ai ruoli degli utenti. Questo funziona.Come propagare il contesto di sicurezza primaverile a JMS?

Le attività asincrone vengono eseguite nei listener JMS (estensione javax.jms.MessageListener). La configurazione di questi ascoltatori è terminata con Spring.

I messaggi vengono inviati dall'applicazione Web, in questo momento l'utente viene autenticato. Ho bisogno della stessa autenticazione nel thread JMS (utente e ruoli) durante l'elaborazione dei messaggi.

Oggi questo viene fatto inserendo l'autenticazione molla nella JMS ObjectMessage:

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

Poi dentro l'ascoltatore JMS dell'oggetto autenticazione viene estratto ed inserito nel contesto:

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

questo funziona La maggior parte delle volte. Ma quando c'è un ritardo prima dell'elaborazione di un messaggio, il messaggio non verrà mai elaborato. Non sono ancora riuscito a determinare la causa della perdita di questi messaggi, ma non sono sicuro che il modo in cui propagiamo l'autenticazione sia buono, anche se funziona in custer quando il messaggio viene elaborato su un altro server.

È questo il modo giusto per propagare un'autenticazione a molla?

saluti, Mickaël

+0

Non ho familiarità con Spring in questo contesto, ma mi sono imbattuto in questo problema prima. Un buon contesto di sicurezza avrà una scadenza oltre la quale non potrà più essere utilizzato. In generale, se il recapito di un messaggio è ritardato oltre la scadenza, il contesto di sicurezza sarà scaduto e il messaggio non verrà elaborato. Questo potrebbe essere il problema qui, nel qual caso aumentare il tempo di scadenza potrebbe risolverlo, o almeno renderlo meno comune. – Alasdair

risposta

0

non ho trovato una soluzione migliore, ma questo funziona per me più che bene.

Con l'invio del messaggio JMS sto memorizzando Authentication come intestazione e rispettivamente ricevendo il contesto di sicurezza ricreato. Per memorizzare Authentication come intestazione si deve puntate come 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; 
    } 
} 

Con l'invio di intestazione del messaggio appena impostato - è possibile creare Decorator per Modello di messaggio, in modo che essa avverrà automaticamente. In te Decorator basta chiamare tale metodo:

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

ricezione diventa più complicato, ma può essere anche eseguita automaticamente. Invece di applicare @EnableJMS uso seguente configurazione:

@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); 
    }  

} 
1

ho implementato per me una soluzione diversa, che sembra più facile per me.

Ho già un convertitore di messaggi, il convertitore di messaggi JSON Jackson standard, che devo configurare sul JMSTemplate e sugli ascoltatori.

Così ho creato un'implementazione MessageConverter che avvolge un altro convertitore di messaggi e propaga il contesto di sicurezza tramite le proprietà del messaggio JMS. (Nel mio caso, il contesto propagato è un token JWT che posso estrarre dal contesto corrente e applicare al contesto di sicurezza del thread di ascolto).

In questo modo l'intera responsabilità per la propagazione del contesto di sicurezza viene elegantemente implementata in una singola classe e richiede solo un po 'di configurazione.

Problemi correlati