Spring STOMP基于令牌的安全性

时间:2017-08-31 05:50:52

标签: java spring stomp

我想为Web套接字连接实现基于令牌的身份验证。

我的后端系统是服务器,机器是客户端,如何在机器和服务器之间发送和接收消息时实现身份验证,此处没有用户身份验证。

@Configuration
@EnableWebSocketMessageBroker
public class MyConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

        @Override
        public Message<?> preSend(Message<?> message, MessageChannel channel) {

            StompHeaderAccessor accessor =
                MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

            if (StompCommand.CONNECT.equals(accessor.getCommand())) {
                String authToken = accessor.getFirstNativeHeader("X-Auth-Token");
                log.debug("webSocket token is {}", authToken);
                Principal user = ... ; // access authentication header(s)
                accessor.setUser(user);
            }

            return message;
        }
    });
  }
}

然而,我完全失去了我在“Principal user = ...;”中的表现。我如何获得令牌的原则?因为这里没有用户存在,只有机器和后端服务器之间的通信

3 个答案:

答案 0 :(得分:0)

猜猜你可以尝试从春季安全上下文中获取主体

SecurityContextHolder.getContext().getAuthentication().getPrincipal();

答案 1 :(得分:0)

听起来您可能需要类似于构建物联网解决方案的人员所使用的解决方案。一个很好的起点是看看OpenID Connect。

这里有一篇很好的文章讨论了相关的挑战和解决方案: https://www.gluu.org/blog/oauth2-for-iot/

OpenId Connect的网站: http://openid.net/connect/

答案 2 :(得分:0)

我知道这个问题已经存在很长时间了,并且在Stack Overflow上已经有很多可用的答案,例如thisthis。这些答案确实解决了这个问题,但是所有人都试图拥有自己的Principle接口的实现,因为Spring Security已经实现了我们所需要的一切,所以不需要。

回答您的问题:需要根据请求中提供的令牌创建原则。您可以使用Principle轻松地将字符串令牌转换为new BearerTokenAuthenticationToken("token-string-without-bearer"),然后再使用authenticationManager.authenticate(bearerTokenAuthenticationToken)对令牌进行身份验证。因此,代码是这样的:

// access authentication header(s)
Principal user = authenticationManager.authenticate(new BearerTokenAuthenticationToken(authToken)); 

configureClientInboundChannel方法的完整实现应该是(很抱歉,我只有Kotlin代码,但是您应该能够理解这个想法):

    override fun configureClientInboundChannel(registration: ChannelRegistration) {
        registration.interceptors(object : ChannelInterceptor {
            override fun preSend(message: Message<*>, channel: MessageChannel): Message<*>? {
                val accessor = MessageHeaderAccessor
                        .getAccessor(message, StompHeaderAccessor::class.java)

                if (accessor != null && accessor.command == StompCommand.CONNECT) {
                    // assume the value of Authorization header has Bearer at the beginning
                    val authorization = accessor.getFirstNativeHeader("Authorization")
                            ?.split(" ", limit = 2)
                            ?: return message

                    // check token type is Bearer
                    if (authorization.getOrNull(0) == "Bearer") {

                        val token = authorization.getOrNull(1)?.takeIf { it.isNotBlank() }
                        // if token exists, authenticate and (if succeeded) then assign user to the session
                        if (token != null) {
                            val user = try {
                                authenticationManager.authenticate(BearerTokenAuthenticationToken(token))
                            } catch (ex: AuthenticationException) {
                                // if throw an exception, do not touch the user header
                                null
                            }
                            if (user != null) {
                                accessor.user = user
                            }
                        }
                    }
                }
                return message
            }
        })
    }

请注意,BearerTokenAuthenticationToken来自org.springframework.boot:spring-boot-starter-oauth2-resource-server,因此您需要此依赖项才能使用它。

authenticationManager来自WebSecurityConfigurerAdapter的实现,它是通过重写authenticationManagerBean方法并将其标记为@Bean作为Bean公开的,然后将Bean注入您的{{ 1}}实现,在本例中为AbstractWebSocketMessageBrokerConfigurer

此外,不要忘记将MyConfig类标记为MyConfig,以便在Spring Security接管之前设置用户标头。