【问题标题】:SpEL evaluation of path variable in WebsocketSecurityConfigurationWebsocketSecurityConfiguration 中路径变量的 SpEL 评估
【发布时间】:2019-12-15 22:10:14
【问题描述】:

我正在运行带有 Websockets 的 JHipster 6.1.2 网关,并试图限制对消息传递主题的访问,以便用户只能订阅他们所属机构的主题。所以基本上我想从订阅路径中检查id

我目前的解决方案是基于https://stackoverflow.com/a/44895369/4246074,看起来如下:

WebsocketSecurityConfiguration.java

@Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        //...
        //User can only subscribe to own institution topic
        .simpSubscribeDestMatchers("/topic/institution.{id}")
        .access("@institutionIdGuard.checkInstitutionId(#id)")
        //...
    }

InstitutionIdGuard.java

@Component
public class InstitutionIdGuard {
    public boolean checkInstitutionId(Long institutionId) {

        //validation logic for institutionId would go here
        return true;
     }

问题: 显然,SpEL 表达式无法从路径访问{id},因为我收到以下日志的空指针错误:

2019-08-08 10:30:08.367 ERROR 31097 --- [   XNIO-1 I/O-1] o.s.w.s.m.StompSubProtocolHandler        : Failed to send client message to application via MessageChannel in session j2a0jlos. Sending STOMP ERROR to client.

org.springframework.messaging.MessageDeliveryException: Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is java.lang.IllegalArgumentException: Failed to evaluate expression '@institutionIdGuard.checkInstitutionId(#id)'
    at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:146)
    at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:122)
    at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient(StompSubProtocolHandler.java:284)
    at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:324)
    at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
    at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
    at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58)
    at org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession.delegateMessages(AbstractSockJsSession.java:386)
    at org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession.handleMessage(WebSocketServerSockJsSession.java:195)
    at org.springframework.web.socket.sockjs.transport.handler.SockJsWebSocketHandler.handleTextMessage(SockJsWebSocketHandler.java:93)
    at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:113)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:42)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:84)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:81)
    at io.undertow.websockets.jsr.FrameHandler$7.run(FrameHandler.java:286)
    at io.undertow.websockets.jsr.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:170)
    at io.undertow.websockets.jsr.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:167)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at io.undertow.websockets.jsr.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:604)
    at io.undertow.websockets.jsr.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:594)
    at io.undertow.websockets.jsr.FrameHandler.invokeTextHandler(FrameHandler.java:266)
    at io.undertow.websockets.jsr.FrameHandler.onFullTextMessage(FrameHandler.java:317)
    at io.undertow.websockets.core.AbstractReceiveListener$2.complete(AbstractReceiveListener.java:156)
    at io.undertow.websockets.core.AbstractReceiveListener$2.complete(AbstractReceiveListener.java:152)
    at io.undertow.websockets.core.BufferedTextMessage.read(BufferedTextMessage.java:105)
    at io.undertow.websockets.core.AbstractReceiveListener.readBufferedText(AbstractReceiveListener.java:152)
    at io.undertow.websockets.core.AbstractReceiveListener.bufferFullMessage(AbstractReceiveListener.java:90)
    at io.undertow.websockets.jsr.FrameHandler.onText(FrameHandler.java:182)
    at io.undertow.websockets.core.AbstractReceiveListener.handleEvent(AbstractReceiveListener.java:44)
    at io.undertow.websockets.core.AbstractReceiveListener.handleEvent(AbstractReceiveListener.java:33)
    at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
    at io.undertow.server.protocol.framed.AbstractFramedChannel$FrameReadListener.handleEvent(AbstractFramedChannel.java:951)
    at io.undertow.server.protocol.framed.AbstractFramedChannel$FrameReadListener.handleEvent(AbstractFramedChannel.java:932)
    at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
    at org.xnio.conduits.ReadReadyHandler$ChannelListenerHandler.readReady(ReadReadyHandler.java:66)
    at org.xnio.nio.NioSocketConduit.handleReady(NioSocketConduit.java:88)
    at org.xnio.nio.WorkerThread.run(WorkerThread.java:561)
Caused by: java.lang.IllegalArgumentException: Failed to evaluate expression '@institutionIdGuard.checkInstitutionId(#id)'
    at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:30)
    at org.springframework.security.messaging.access.expression.MessageExpressionVoter.vote(MessageExpressionVoter.java:57)
    at org.springframework.security.messaging.access.expression.MessageExpressionVoter.vote(MessageExpressionVoter.java:39)
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:63)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
    at org.springframework.security.messaging.access.intercept.ChannelSecurityInterceptor.preSend(ChannelSecurityInterceptor.java:69)
    at org.springframework.messaging.support.AbstractMessageChannel$ChannelInterceptorChain.applyPreSend(AbstractMessageChannel.java:178)
    at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:132)
    ... 37 common frames omitted
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method checkInstitutionId(null) cannot be found on type com.mycompany.websocketgateway.security.InstitutionIdGuard
    at org.springframework.expression.spel.ast.MethodReference.findAccessorForMethod(MethodReference.java:225)
    at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:134)
    at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:54)
    at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:390)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:90)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:114)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:300)
    at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:26)
    ... 44 common frames omitted

如果您有任何想法如何使我的解决方案发挥作用,或在id 中执行检查的其他其他方式,我将不胜感激。

【问题讨论】:

    标签: spring-boot spring-security websocket jhipster spring-el


    【解决方案1】:

    我在Spring Security issue tracker 上找到了解决方案。显然在 Spring Security 5.2 之前,您可以将隐式 message 变量传递给 SpEL 表达式

    .simpSubscribeDestMatchers("/topic/institution.*")
        .access("@institutionIdGuard.checkInstitutionId(authentication, message)")
    

    然后在验证方法中可以从消息中获取路径并使用它进行自己的验证:

    public boolean checkInstitutionId(Authentication authentication, Message<?> message) {
            StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);
            String topic = sha.getDestination();
            String id = topic.replace("/topic/institution/", "");
    
            //validation logic for institutionId would go here
            return true;
    }
    

    Spring Security 5.2 应该已经根据this pull request 修复了这个问题。

    【讨论】:

      猜你喜欢
      • 2018-09-01
      • 2020-11-20
      • 2018-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-02
      • 2022-01-24
      相关资源
      最近更新 更多