2013-09-24 2 views
6

У меня есть веб-приложение, которое устанавливает весенний контекст безопасности через пружинный фильтр. Услуги защищены весенними аннотациями на основе ролей пользователей. Это работает.Как распространять весенний контекст безопасности для JMS?

асинхронные задачи выполняются в JMS слушателей (расширить javax.jms.MessageListener). Настройка этих слушателей выполняется с помощью Spring.

Сообщения отправляются из веб-приложения, в это время пользователь аутентифицирован. Мне нужна такая же аутентификация в потоке JMS (пользователь и роли) во время обработки сообщений.

Сегодня это делается путем ввода аутентификации пружины в 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); 

это работает большую часть времени. Но когда перед обработкой сообщения происходит задержка, сообщение никогда не будет обработано. Я еще не мог определить причину потери этих сообщений, но я не уверен, что способ распространения аутентификации хорош, даже если он работает в custer, когда сообщение обрабатывается на другом сервере.

Это правильный способ распространения проверки подлинности пружин?

С уважением, Микаэль

+0

Я не знаком с весной в этом контексте, но раньше я сталкивался с этой проблемой. Хороший контекст безопасности будет иметь время истечения срока действия, после которого его больше нельзя будет использовать. В общем случае, если доставка сообщения задерживается до истечения срока действия, срок действия службы безопасности истек и сообщение не обрабатывается. Это может быть проблемой здесь, и в этом случае увеличение этого срока истечения может исправить это или, по крайней мере, сделать его менее распространенным. – Alasdair

ответ

0

Я не нашел лучшего решения, но это один работает для меня просто отлично.

Посылая из JMS сообщения I'am хранения 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; 
    } 
} 

Посылая просто установить заголовок сообщения - вы можете создать Декоратор для сообщений Шаблон, так что это будет происходить автоматически. В вас декоратор просто назовете такой метод:

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

} 
1

я реализовал для себя другое решение, которое кажется мне легче.

Уже у меня есть сообщение конвертер, стандарт JSON Джексон сообщение конвертер, который мне нужно настроить на JMSTemplate и слушателей.

Так что я создал реализацию MessageConverter который оборачивает вокруг другого преобразователя сообщений и распространяющийся контекст безопасности через свойство сообщения JMS. (В моем случае распространяемый контекст - это токен JWT, который я могу извлечь из текущего контекста и применить к контексту безопасности прослушивающего потока).

Таким образом, вся ответственность за распространение контекста безопасности элегантно реализуется в одном классе, и требует лишь немного конфигурации.

Смежные вопросы