Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/423.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 将JWT令牌传递给SockJS_Javascript_Java_Spring Boot_Websocket_Sockjs - Fatal编程技术网

Javascript 将JWT令牌传递给SockJS

Javascript 将JWT令牌传递给SockJS,javascript,java,spring-boot,websocket,sockjs,Javascript,Java,Spring Boot,Websocket,Sockjs,我需要在与SockJS握手时发送令牌。我尝试了许多建议的实现,但调用了相同的异常 java.lang.IllegalArgumentException: JWT String argument cannot be null or empty. 在后端WebSocketConfig @Configuration @EnableWebSocketMessageBroker @CrossOrigin public class WebSocketConfig implements WebSocketM

我需要在与SockJS握手时发送令牌。我尝试了许多建议的实现,但调用了相同的异常

java.lang.IllegalArgumentException: JWT String argument cannot be null or empty.
在后端
WebSocketConfig

@Configuration
@EnableWebSocketMessageBroker
@CrossOrigin
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/socket");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket").withSockJS();
    }
}
尝试与套接字建立连接的函数。纯javascript

function connect() {
    var socket = new SockJS('http://localhost:8889/websocket',
             null,
            {
                transports: ['xhr-streaming'], 
                headers: {'Authorization': 'Bearer eyJhbGciOiJIUzUxMiJ9...' }
            });
    stompClient = Stomp.over(socket);
    stompClient.connect({},function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/socket/event', function (greeting) {
            showGreeting(JSON.parse(greeting.body).content);
        });
    });
}
问题在于握手,这些头似乎没有正确地传递令牌。我尝试过很多不同的握手方式,但在我的例子中找不到正确的方式

在握手后尝试使用头之前,我从这里得到了实现想法,但我发现它需要立即使用令牌

编辑:添加WebSecurity配置

@Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
        .cors()
        .configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
        .and()
        .csrf()
        .disable()
        .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()

        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
        .authorizeRequests()
        .antMatchers("/login/**").permitAll()
        .antMatchers("/websocket/**").permitAll()
        .anyRequest().authenticated();
        // Custom JWT based security filter
        JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(userDetailsService(), jwtTokenUtil, tokenHeader);
        httpSecurity
        .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }//end configure(HttpSecurity)
createAuthenticationToken

@ApiOperation(value = "Login with the user credentials",
            response = JwtAuthenticationResponse.class)
    @ApiResponses(value = {
            @ApiResponse(code = 401, message = "Unauthorized"),
            @ApiResponse(code = 404, message = "Not Found",response = ExceptionResponse.class),
            @ApiResponse(code = 400, message = "Bad Request",response = ExceptionResponse.class),
            @ApiResponse(code = 200 , message = "OK", response = JwtAuthenticationResponse.class)
    })
    @RequestMapping(value = "${jwt.route.authentication.path}", method = RequestMethod.POST)
    public ResponseEntity<?> createAuthenticationToken(
            @ApiParam(value = "User's email and password", required = true)
            @RequestBody JwtAuthenticationRequest authenticationRequest) 
            throws AuthenticationException {
        ResponseEntity<?> response;
        //authenticate the user
        final User user = userService.getByEmail(authenticationRequest.getEmail());
        try {
            authenticate(user.getUsername(), authenticationRequest.getPassword(),user.getId(),user.getAuthority().getName());
            // Reload password post-security so we can generate the token
            final UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
            final String token = jwtTokenUtil.generateToken(userDetails);
            // Return the token
            response  = ResponseEntity.ok(new JwtAuthenticationResponse(token,user.getUsername(),user.getFirstName(),user.getLastName(),
                    user.getEmail(),user.getId(),user.getAuthority().getName(),jwtTokenUtil.getExpirationTime(token)));
        }catch(NullPointerException e) {
            response = new ResponseEntity<>(new ExceptionResponse(404,"User Not Found","Authentication Failure"),HttpStatus.NOT_FOUND);
        }catch(AuthenticationException e) {
            response = new ResponseEntity<>(new ExceptionResponse(400,"Invalid E-mail or Password","Authentication Failure"),HttpStatus.BAD_REQUEST);
        }//end try
                return response;
    }//end createAuthenticationToken(JwtAuthenticationRequest)

Websocket在HTTP的头中不遵循相同的模式。这就是为什么,即使您在头中发送令牌,也找不到它。我以前也遇到过同样的问题,我更改了websocket安全结构

我的示例代码如下:

@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
    registration.setInterceptors(new ChannelInterceptorAdapter() {

        @Override
        public Message<?> preSend(Message<?> message, MessageChannel channel) {
            StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
            MessageHeaders headers = message.getHeaders();
            SimpMessageType type = (SimpMessageType) headers.get("simpMessageType");
            List<String> tokenList = accessor.getNativeHeader("Authorization");
            String token = null;
            if(tokenList == null || tokenList.size() < 1) {
                return message;
            } else {
                token = tokenList.get(0);
                if(token == null) {
                    return message;
                }
            }

            // validate and convert to a Principal based on your own requirements e.g.
            // authenticationManager.authenticate(JwtAuthentication(token))
            try{
                JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(new RawAccessJwtToken(tokenExtractor.extract(token)));
                Authentication yourAuth = jwtAuthenticationProvider.authenticate(jwtAuthenticationToken);
                accessor.setUser(yourAuth);
            } catch (Exception e) {
                throw new IllegalArgumentException(e.getMessage());
            }




            // not documented anywhere but necessary otherwise NPE in StompSubProtocolHandler!
            accessor.setLeaveMutable(true);
            return MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
        }
    });

}
@覆盖
公共无效配置ClientInBoundChannel(通道注册){
registration.setInterceptors(新的ChannelInterceptorAdapter(){
@凌驾
公共消息呈现(消息消息、消息通道){
StompHeaderAccessor访问器=StompHeaderAccessor.wrap(消息);
MessageHeaders=message.getHeaders();
SimpMessageType=(SimpMessageType)headers.get(“SimpMessageType”);
List tokenList=accessor.getNativeHeader(“授权”);
字符串标记=null;
if(tokenList==null | | tokenList.size()<1){
返回消息;
}否则{
token=tokenList.get(0);
if(标记==null){
返回消息;
}
}
//根据您自己的要求验证并转换为委托人,例如。
//authenticationManager.Authentication(JwtAuthentication(令牌))
试一试{
JwtAuthenticationToken JwtAuthenticationToken=新的JwtAuthenticationToken(新的RawAccessJwtToken(tokenExtractor.extract(token));
Authentication yourAuth=jwtAuthenticationProvider.authenticate(jwtAuthenticationToken);
accessor.setUser(yourAuth);
}捕获(例外e){
抛出新的IllegalArgumentException(e.getMessage());
}
//未在任何地方记录,但必须在StompSubtocolHandler中记录NPE!
accessor.setLeaveMutable(true);
返回MessageBuilder.createMessage(message.getPayload(),accessor.getMessageHeaders());
}
});
}

用于注册自定义身份验证侦听器的服务器端配置。请注意,拦截器只需对CONNECT消息进行身份验证并设置用户头。Spring记录并保存经过身份验证的用户,并将其与同一会话上的后续STOMP消息相关联。以下示例显示如何注册自定义身份验证侦听器:

  @Configuration
    @EnableWebSocketMessageBroker
    public class MyConfig implements WebSocketMessageBrokerConfigurer {

        @Override
        public void configureClientInboundChannel(ChannelRegistration registration) {
            registration.interceptors(new ChannelInterceptor() {
                @Override
                public Message<?> preSend(Message<?> message, MessageChannel channel) {
                    StompHeaderAccessor accessor =
                            MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
                    if (StompCommand.CONNECT.equals(accessor.getCommand())) {
                        Authentication user = ... ; // access authentication header(s)
                        accessor.setUser(user);
                    }
                    return message;
                }
            });
        }
    }
@配置
@EnableWebSocketMessageBroker
公共类MyConfig实现WebSocketMessageBrokerConfiger{
@凌驾
公共无效配置ClientInBoundChannel(通道注册){
注册.拦截器(新的ChannelInterceptor(){
@凌驾
公共消息呈现(消息消息、消息通道){
StompHeaderAccessor存取器=
MessageHeaderAccessor.getAccessor(message,StompHeaderAccessor.class);
if(StompCommand.CONNECT.equals(accessor.getCommand())){
身份验证用户=…;//访问身份验证标头
accessor.setUser(用户);
}
返回消息;
}
});
}
}
另外,请注意,当您目前使用Spring Security的消息授权时,您需要确保认证ChannelInterceptor配置的顺序早于Spring Security。最好是在自己的WebSocketMessageBrokerConfigure实现中声明自定义拦截器,该拦截器用@Order(Ordered.HIGHEST_priority+99)标记

另一种方式: 同样,SockJS JavaScript客户端也不提供通过SockJS传输请求发送HTTP头的方法。如您所见,sockjs客户端版本196。相反,它允许发送查询参数,您可以使用这些参数发送令牌,然后使用Spring设置一些过滤器,使用提供的令牌识别会话,但这也有其自身的缺点(例如,令牌可能无意中与服务器日志中的URL一起记录)


在哪一行获得了
IllegalArgumentException
/是否有异常的所有堆栈跟踪?@smilyface我在WebSecurityConfig上添加了configure方法。每次调用方法底部的
addFilterBefore
,都会发生这种情况。这是有道理的,因为当我进行HTTP调用时也会发生同样的事情,并且没有令牌。但是对于SockJS,我试图传递令牌,但它仍然没有到达后端。websocket通信正在进行,没有任何问题。嗯,没关系。但是你没有回答我的问题(至少10-15行堆栈跟踪)@smilyface我在pastebin上添加了完整的堆栈跟踪。同样的例外情况已经发生了四次。可能是标头触发了异常。还提供了创建令牌的方法(如果可能,还提供了一个示例令牌)。我想可能是这样的
Jwts.builder().something..
创建令牌。请在问题中也包括这一点。我根据您的实现实现了该方法,但遗憾的是,该方法似乎没有被调用。也许我错过了什么。编辑:看来它毕竟已经被调用了,我会弄清楚它是如何工作的,并且会得到更多
  @Configuration
    @EnableWebSocketMessageBroker
    public class MyConfig implements WebSocketMessageBrokerConfigurer {

        @Override
        public void configureClientInboundChannel(ChannelRegistration registration) {
            registration.interceptors(new ChannelInterceptor() {
                @Override
                public Message<?> preSend(Message<?> message, MessageChannel channel) {
                    StompHeaderAccessor accessor =
                            MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
                    if (StompCommand.CONNECT.equals(accessor.getCommand())) {
                        Authentication user = ... ; // access authentication header(s)
                        accessor.setUser(user);
                    }
                    return message;
                }
            });
        }
    }