当前位置: 首页 > 面试题库 >

Spring中的Websocket身份验证和授权

萧浩漫
2023-03-14
问题内容

我一直在努力用Spring-Security 正确实现Stomp(websocket) 身份验证授权
为了后代,我将回答我自己的问题以提供指导。

问题

Spring WebSocket文档(用于身份验证)看起来不清楚ATM(IMHO)。而且我不明白如何正确处理 身份验证授权

我想要的是

  • 使用登录名/密码对用户进行身份验证。
  • 防止匿名用户通过WebSocket连接。
  • 添加授权层(用户,管理员等)。
  • Principal在控制器可用。

我不想要的

  • 在HTTP协商端点上进行身份验证(因为大多数JavaScript库都不随HTTP协商调用一起发送身份验证标头)。

问题答案:

如上所述,文档(ATM)尚不清楚(IMHO),直到Spring提供一些清晰的文档为止,这是一个样板,可避免您花两天的时间试图了解安全链的功能

Rob-Leggett做出了非常不错的尝试,但他在Springs上课了,所以我感到不舒服。

要知道的事情:

  • __http和WebSocket的 安全链安全配置 是完全独立的。
  • Spring AuthenticationProvider完全不参与Websocket身份验证。
  • 不会在HTTP协商终结点上进行身份验证,因为没有JavaScript STOMP(Websocket)将身份验证标头与HTTP请求一起发送。
  • 在CONNECT请求上设置后, 用户simpUser)将存储在websocket会话中,以后的消息将不再需要身份验证。

Maven部门

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-messaging</artifactId>
</dependency>

WebSocket配置

下面的配置注册一个简单的消息代理(请注意,它与身份验证或授权无关)。

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(final MessageBrokerRegistry config) {
        // These are endpoints the client can subscribes to.
        config.enableSimpleBroker("/queue/topic");
        // Message received with one of those below destinationPrefixes will be automatically router to controllers @MessageMapping
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(final StompEndpointRegistry registry) {
        // Handshake endpoint
        registry.addEndpoint("stomp"); // If you want to you can chain setAllowedOrigins("*")
    }
}

Spring安全配置

由于Stomp协议依赖于第一个HTTP请求,因此我们需要授权对脚踏握手端点的HTTP调用。

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        // This is not for websocket authorization, and this should most likely not be altered.
        http
                .httpBasic().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests().antMatchers("/stomp").permitAll()
                .anyRequest().denyAll();
    }
}

然后,我们将创建一个负责验证用户身份的服务。

@Component
public class WebSocketAuthenticatorService {
    // This method MUST return a UsernamePasswordAuthenticationToken instance, the spring security chain is testing it with 'instanceof' later on. So don't use a subclass of it or any other class
    public UsernamePasswordAuthenticationToken getAuthenticatedOrFail(final String  username, final String password) throws AuthenticationException {
        if (username == null || username.trim().isEmpty()) {
            throw new AuthenticationCredentialsNotFoundException("Username was null or empty.");
        }
        if (password == null || password.trim().isEmpty()) {
            throw new AuthenticationCredentialsNotFoundException("Password was null or empty.");
        }
        // Add your own logic for retrieving user in fetchUserFromDb()
        if (fetchUserFromDb(username, password) == null) {
            throw new BadCredentialsException("Bad credentials for user " + username);
        }

        // null credentials, we do not pass the password along
        return new UsernamePasswordAuthenticationToken(
                username,
                null,
                Collections.singleton((GrantedAuthority) () -> "USER") // MUST provide at least one role
        );
    }
}

注意:UsernamePasswordAuthenticationToken 必须
具有GrantedAuthorities,如果您使用其他构造函数,Spring会自动设置isAuthenticated = false

几乎在那儿,现在我们需要创建一个拦截器,该拦截器将设置“ simpUser”标头或在CONNECT消息上抛出“
AuthenticationException”。

@Component
public class AuthChannelInterceptorAdapter extends ChannelInterceptor {
    private static final String USERNAME_HEADER = "login";
    private static final String PASSWORD_HEADER = "passcode";
    private final WebSocketAuthenticatorService webSocketAuthenticatorService;

    @Inject
    public AuthChannelInterceptorAdapter(final WebSocketAuthenticatorService webSocketAuthenticatorService) {
        this.webSocketAuthenticatorService = webSocketAuthenticatorService;
    }

    @Override
    public Message<?> preSend(final Message<?> message, final MessageChannel channel) throws AuthenticationException {
        final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

        if (StompCommand.CONNECT == accessor.getCommand()) {
            final String username = accessor.getFirstNativeHeader(USERNAME_HEADER);
            final String password = accessor.getFirstNativeHeader(PASSWORD_HEADER);

            final UsernamePasswordAuthenticationToken user = webSocketAuthenticatorService.getAuthenticatedOrFail(username, password);

            accessor.setUser(user);
        }
        return message;
    }
}

注意:preSend() 必须 返回a
UsernamePasswordAuthenticationToken,Spring安全链中的另一个元素对此进行测试。请注意:如果您的UsernamePasswordAuthenticationToken构建未通过GrantedAuthority,则身份验证将失败,因为未经授权的构造函数会自动设置“
authenticated = false 这是重要细节”,而spring-security中未对此进行记录

最后,再创建两个类以分别处理授权和身份验证。

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketAuthenticationSecurityConfig extends  WebSocketMessageBrokerConfigurer {
    @Inject
    private AuthChannelInterceptorAdapter authChannelInterceptorAdapter;

    @Override
    public void registerStompEndpoints(final StompEndpointRegistry registry) {
        // Endpoints are already registered on WebSocketConfig, no need to add more.
    }

    @Override
    public void configureClientInboundChannel(final ChannelRegistration registration) {
        registration.setInterceptors(authChannelInterceptorAdapter);
    }

}

请注意:@Orderis CRUCIAL 别忘了它,它使我们的拦截器可以在安全链中首先注册。

@Configuration
public class WebSocketAuthorizationSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
    @Override
    protected void configureInbound(final MessageSecurityMetadataSourceRegistry messages) {
        // You can customize your authorization mapping here.
        messages.anyMessage().authenticated();
    }

    // TODO: For test purpose (and simplicity) i disabled CSRF, but you should re-enable this and provide a CRSF endpoint.
    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }
}


 类似资料:
  • 我一直在努力用Spring-Security正确地实现Stomp(websocket)身份验证和授权。对于后人,我将回答我自己的问题,以提供一个指导。 Spring WebSocket文档(用于身份验证)看起来不清楚ATM(IMHO)。我无法理解如何正确处理身份验证和授权。 null 在HTTP协商endpoint上进行身份验证(因为大多数JavaScript库不会随HTTP协商调用一起发送身份验

  • 我正在实现一个基于Spring Data REST的应用程序,我想知道是否有一种优雅的方法可以使用此框架或相关框架来实现身份验证和授权规则。 对REST服务器的所有HTTP请求都必须带有身份验证标头,我需要检查它们并根据HTTP方法和经过身份验证的用户与所请求资源的关联来决定是否授权。例如,(该应用程序是电子学习系统的REST服务器),讲师只能访问他们自己的课程部分,学生只能访问他们订阅的课程部分

  • 我不熟悉spring微服务世界。由于我处于学习阶段,我尝试并实施了以下内容。 > 路由(能够使用Spring云网关进行路由) 负载平衡(Netflix Eureka) 限速和断路器 我只需要一些澄清和建议,说明在这些情况下该怎么做: 因为我已经创建了身份验证/授权作为一个单独的微服务集中。现在我如何实现这样的每个请求必须包含jwt令牌和通过API网关调用其他微服务也应该检查哪个用户有权限访问其他微

  • 我有一个JavaJSF 2,Spring 3,HiberNate 4JavaEE应用程序,它使用第三方库来验证用户。我将所需的CA证书导入到我的JVM中,将第三个库添加到项目中,并在web.xml.中配置该库从智能卡读取用户详细信息。整个设置正在工作,第三方库将用户带到主页。 以下是我的要求,以确保应用程序。 再次检查该用户是否存在于特定于应用程序的数据库中 我看了这个链接,似乎“身份验证处理过滤

  • 在Spring Security am中,使用DefaultJaasAuthenticationProvider配置进行带有linux用户名/密码的登录身份验证。JpamLoginModule用于身份验证。我成功地进行了身份验证,但我在授权方面有问题(ROLE_USER,ROLE_ADMIN),正在获得HTTP状态403-访问被拒绝错误。 下面是我在spring-security.xml中使用的配