2013-09-24 4 views
6

Ich habe eine Webanwendung, die einen Federsicherheitskontext durch einen Federfilter einstellt. Die Dienste sind mit Spring-Anmerkungen basierend auf Benutzerrollen geschützt. Das funktioniert.Wie verbreitet man den Sicherheitskontext für Spring auf JMS?

Asynchrone Tasks werden in JMS-Listenern ausgeführt (extend javax.jms.MessageListener). Die Einrichtung dieser Listener erfolgt mit Spring.

Nachrichten werden von der Webanwendung gesendet, zu diesem Zeitpunkt wird ein Benutzer authentifiziert. Ich benötige die gleiche Authentifizierung im JMS-Thread (Benutzer und Rollen) während der Nachrichtenverarbeitung.

Heute ist dies, indem sie die Feder-Authentifizierung in der JMS Object getan:

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

innerhalb der JMS-Listener dann das Authentifikationsobjekt wird im Rahmen extrahiert und eingestellt:

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

Dies funktioniert meistens. Wenn es jedoch eine Verzögerung vor der Verarbeitung einer Nachricht gibt, wird die Nachricht niemals verarbeitet. Ich konnte die Ursache dieses Nachrichtenverlustes noch nicht bestimmen, aber ich bin mir nicht sicher, wie wir die Authentifizierung verbreiten, auch wenn sie in custer funktioniert, wenn die Nachricht auf einem anderen Server verarbeitet wird.

Ist dies der richtige Weg, um eine Feder-Authentifizierung zu propagieren?

Grüße, Mickaël

+0

Ich bin nicht vertraut mit Spring in diesem Zusammenhang, aber ich bin auf dieses Problem schon einmal gestoßen. Ein guter Sicherheitskontext hat eine Ablaufzeit, nach der er nicht mehr verwendet werden kann. Wenn eine Nachrichtenzustellung nach dem Ablauf verzögert wird, ist der Sicherheitskontext im Allgemeinen abgelaufen und die Nachricht wurde nicht verarbeitet. Dies könnte das Problem sein, in welchem ​​Fall die Erhöhung der Ablaufzeit dies beheben oder zumindest weniger häufig machen kann. – Alasdair

Antwort

0

ich nicht bessere Lösung gefunden haben, aber diese funktioniert nur für mich in Ordnung.

Durch Senden der JMS-Nachricht speichere ich Authentication als Header und empfange jeweils einen neuen Sicherheitskontext. Um Authentication als Kopfzeile zu speichern, müssen Sie es als Base64 serialise:

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

Durch das Senden nur die Message-Header - Sie Ausstatter für Nachrichtenvorlage erstellen können, so dass es automatisch geschehen wird. In Ihrem Dekorateur rufen Sie einfach folgende Methode an:

Empfangen wird komplizierter, aber es kann auch automatisch erfolgen. Anstelle der Anwendung @EnableJMS Verwendung folgende Konfiguration:

@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

ich mir eine andere Lösung umgesetzt haben, was für mich einfacher zu sein scheint.

Ich habe bereits einen Nachrichtenkonverter, den Standard-JSON-Jackson-Nachrichtenkonverter, den ich auf dem JMSTemplate und den Listenern konfigurieren muss.

Also habe ich eine MessageConverter-Implementierung erstellt, die einen anderen Nachrichtenkonverter umschließt und den Sicherheitskontext über die JMS-Nachrichteneigenschaften weiterleitet. (In meinem Fall ist der propagierte Kontext ein JWT-Token, das ich aus dem aktuellen Kontext extrahieren und auf den Sicherheitskontext des Listening-Threads anwenden kann).

Auf diese Weise wird die gesamte Verantwortung für die Verbreitung von Sicherheitskontexten elegant in einer einzigen Klasse implementiert und erfordert nur ein wenig Konfiguration.

Verwandte Themen