Cometd, Spring-security: аутентифицированный в данный момент пользователь недоступен в Listener - PullRequest
0 голосов
/ 12 октября 2018

Я работаю над проектом Spring-MVC, где я использую Spring-security для аутентификации и авторизации.Наряду с этим я использую библиотеку Cometd для отправки сообщений через веб-сокеты.После получения сообщения в Слушателе, когда я пытаюсь получить аутентифицированного пользователя, оно всегда равно нулю.

Как я могу убедиться, что каждый запрос в Cometd по крайней мере содержит JSESSIONID, который требуется Spring-security для идентификации?

Или в Spring-security есть некоторые настройки, которые могут сделать это возможным.

Когда я посмотрел, есть много пользователей, сталкивающихся с этой проблемой, но нет точного ответа или кода, который был бы полезен.

Тестовый код:

  @Listener(value = "/service/testlistener/{id}")
    public void testlistener(ServerSession remote,
                             ServerMessage message, @Param("id") String id) {
        try {            
          Person user = this.personService.getCurrentlyAuthenticatedUser();
            Map<String, Object> data = message.getDataAsMap();
           System.out.println("currentlyAuthenticatedUser: " + user + " \nTransmitted Data in map:" + data.get("name"));
            Map<String, Object> output = new HashMap<>();
            output.put("name", user.getFirstName());
            ServerChannel serverChannel = bayeux.createChannelIfAbsent("/person/" + id).getReference();
            serverChannel.setPersistent(false);
            serverChannel.publish(serverSession, output);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

cometd.js:

 var connectionIntervall = null;
    var onlineElement = document.getElementById("browser-online");
    var cometd = $.cometd;
    cometd.configure({
        url: navigationController.generateCometDUrl(),
        logLevel: 'error',
        stickyReconnect: false,
        appendMessageTypeToURL: false,
        requestHeaders : navigationController.generateCometDHeaders()
    });

    var connects = 0;

    cometd.addListener('/meta/handshake', _metaHandshake);
    cometd.addListener('/meta/connect', _metaConnect);
    cometd.websocketEnabled = true;

    cometd.handshake();

1 Ответ

0 голосов
/ 30 января 2019

Решение для: - CometD 4.0.2 - Spring 4.3.9.RELEASE - Spring Security 4.1.0.RELEASE

В моем проекте мы столкнулись с аналогичной проблемой при переходе от «длинного пула» к «веб-сокету»подключение.Для пояснения - одно отличие - мы использовали «пользовательский контекст» только во время «рукопожатия».

При использовании «длинного пула» Cookie с JSESSIONID правильно интерпретировался Spring, и мы работали в «пользовательском контексте».После переключения на «websocket» это выглядело так, как будто «пользовательский контекст» был потерян непосредственно перед запуском кода получения сообщений.

Мы не смогли найти правильное решение - следующий код, на мой взгляд, просто хак.Но это сработало для нас.

import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.DefaultSecurityPolicy;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;

public class BayeuxAuthenticator extends DefaultSecurityPolicy implements ServerSession.RemoveListener {

    // (...)

    @Override
    public boolean canHandshake( BayeuxServer server, ServerSession session, ServerMessage message ) {
        SecurityContextHolder.getContext().setAuthentication( ( UsernamePasswordAuthenticationToken ) message.getBayeuxContext().getUserPrincipal() );
        String user = securityService.getUserName();
        // (...)

        return true;
    }

    // (...)
}

Где следующая строка является наиболее важной

SecurityContextHolder.getContext().setAuthentication( ( UsernamePasswordAuthenticationToken ) message.getBayeuxContext().getUserPrincipal() );

Редактировать 2:

Мы получаем данныеиз

String user = securityService.getUserName();

, где securityService - это Spring @Service, защищенный Spring @Secured (/ * ожидаемые роли * /).(Аннотация @Secured создала нам исключения авторизации перед настройкой контекста)

Внутри есть чтение контекста и пользовательские данные:

SecurityContextHolder.getContext().getUserPrincipal().getName()

Но, честно говоря, если вам просто нужны "принципалы пользователя"может быть достаточно прочитать его из:

message.getBayeuxContext().getUserPrincipal() // instanceof java.security.Principal

Который в нашем случае был типа

org.springframework.security.authentication.UsernamePasswordAuthenticationToken

Может быть, в вашем случае это другой подкласс?

Дляразъяснение - во время запуска соединения через веб-сокет мы передали Cookie с правильным JSESSIONID в запросе.

Запрос на соединение WebSocket:

Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,pl;q=0.8
Cache-Control: no-cache
Connection: Upgrade
Cookie: XSRF-TOKEN=<some token>; Idea-<some token>; lastOpenTab=sourcing/content; io=<some token>; BAYEUX_BROWSER=<some token>; JSESSIONID=<some token>
Host: localhost:8090
Origin: http://localhost:8090
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: NTtWtXAL8ReIB0LjvI2G0g==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 ...
...