spring-security 如何访问ChannelInterceptor中设置的WebSocket的Principal?Principal为空

daupos2t  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(349)

我正在尝试从我的@MessageMapping注解方法中的SimpMessageHeaderAccessor获取主体。
正在使用连接上的ChannelInterceptor设置主体。
即使当我console.log连接帧并看到“user-name”头时,在我的@MessageMapping方法中,Principal始终为空。
感谢您的任何建议:)
消息控制器类:

@Controller
public class MessageController {

    private SimpMessagingTemplate template;

    public MessageController(SimpMessagingTemplate template) {
        this.template = template;
    }

    @MessageMapping("/private")
    public void message(SimpMessageHeaderAccessor accessor, @Payload Message message){
        System.out.println(accessor.getUser().getName());
    }

}

通道拦截器类别:

@Service
public class CustomHandshakeInterceptor implements ChannelInterceptor {

    private JwtUtil jwtUtil;

    public CustomHandshakeInterceptor(JwtUtil jwtUtil) {
        this.jwtUtil = jwtUtil;
    }

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        if(StompCommand.CONNECT.equals(accessor.getCommand())){
            Object raw = message.getHeaders().get(SimpMessageHeaderAccessor.NATIVE_HEADERS);
            if(raw instanceof Map){
                Object token = ((Map) raw).get("JWT_TOKEN");
                if(token instanceof ArrayList){
                    System.out.println(((ArrayList<?>) token).get(0).toString());
                    accessor.setUser(new Principal() {
                        @Override
                        public String getName() {
                            return jwtUtil.extractUsername(((ArrayList<?>) token).get(0).toString());
                        }
                    });
                }
            }
            System.out.println("FROM INTERCEPTOR " + accessor.getUser().getName());
        }
        return message;
    }
}

套接字配置类:

@Configuration
@EnableWebSocketMessageBroker
public class SocketConfig implements WebSocketMessageBrokerConfigurer {

    private CustomHandshakeInterceptor handshakeInterceptor;

    public SocketConfig(CustomHandshakeInterceptor handshakeInterceptor) {
        this.handshakeInterceptor = handshakeInterceptor;
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/stomp")
                .setAllowedOrigins("http://localhost:3000")
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("queue");
        registry.setApplicationDestinationPrefixes("/app");
        registry.setUserDestinationPrefix("/users");
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(handshakeInterceptor);
    }
}

客户端主控台:

Frame
body: ""
command: "CONNECTED"
headers: {user-name: 'david', heart-beat: '0,0', version: '1.1'}
[[Prototype]]: Object

错误消息:

java.lang.NullPointerException: Cannot invoke "java.security.Principal.getName()" because the return value of "org.springframework.messaging.simp.SimpMessageHeaderAccessor.getUser()" is null
    at eu.filip.backend.socket.MessageController.message(MessageController.java:28) ~[classes/:na]
cgh8pdjw

cgh8pdjw1#

所以在YouTube和Google上查找之后,我找到了DefaultHandshakeHandler类,它有一个返回Principal的determineUser方法。我基本上自己实现了Principal赋值,并通过SocketConfig registerStompEndpoints.setHandshakeHandler()方法添加了它。
在这样做之后,我能够在@MessageMapping注解方法中检索我的Principal!

相关问题