【问题标题】:Cometd, Spring-security : Currently authenticated user not available inside a ListenerCometd,Spring-security:当前经过身份验证的用户在侦听器中不可用
【发布时间】:2019-03-17 16:09:51
【问题描述】:

我正在开发一个 Spring-MVC 项目,我正在使用 Spring-security 进行身份验证和授权。除此之外,我正在使用 Cometd 库通过 websockets 发送消息。在侦听器中收到消息后,当我尝试获取当前经过身份验证的用户时,它始终为空。

如何确保 Cometd 中的每个请求至少包含 Spring-security 识别所需的 JSESSIONID?

或者 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();

【问题讨论】:

    标签: java spring spring-security comet cometd


    【解决方案1】:

    解决方案: -彗星D 4.0.2 - 春季 4.3.9.RELEASE - Spring Security 4.1.0.RELEASE

    在我的项目中,我们在从“长池”切换到“websocket”连接时遇到了类似的问题。为了澄清 - 一个区别 - 我们只在“握手”期间使用“用户上下文”。

    使用带有 JSESSIONID 的“长池”Cookie 被 Spring 正确解释,我们在“用户上下文”中工作。切换到“websocket”后,看起来“用户上下文”在运行消息接收代码之前丢失了。

    我们找不到合适的解决方案 - 我认为以下代码只是一个 hack。但它对我们有用。

    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
    

    也许在你的情况下它是不同的子类?

    为了澄清 - 在网络套接字连接启动期间,我们在请求中传递了带有正确 JSESSIONID 的 Cookie。

    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 ...
    

    【讨论】:

    • 谢谢。我希望这可行,但消息对象不包含任何方法 message.getBayeuxContext()。我们正在使用 cometd 3.1.2
    • 我的错。我在解决方案中添加了版本。
    • 哦,那我们可以升级到你的版本试试看。明天早上会检查它,因为办公室现在已经结束并恢复。谢了哥们。 :-D
    • 我升级了库,但我仍然得到一个用户为 null 的 NPE。你能告诉我你如何在你身边检索当前经过身份验证的用户吗?谢谢。
    • 我将响应添加为“编辑 2”。我还在帖子开头添加了有关 Spring 版本的信息。也许您应该检查从 BayeuxContext 返回的 Principals 是否不为空?如果是这样,通信中是否传递了 JSESSIONID Cookie?您可以在帖子末尾添加 WebSocket 连接请求。请记住我在 BayeuxAuthenticator 中执行此操作 - 也许它在其他通信中不可用? :(如果您找到解决方案,请在此处发布。也许有一天我会需要它;)
    猜你喜欢
    • 2023-03-27
    • 2015-10-28
    • 2014-12-20
    • 2016-12-29
    • 1970-01-01
    • 2017-09-21
    • 2015-08-09
    • 2014-10-03
    • 2013-04-23
    相关资源
    最近更新 更多