栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

Spring中的Websocket身份验证和授权

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring中的Websocket身份验证和授权

如上所述,文档(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@EnableWebSocketMessageBrokerpublic 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调用。

@Configurationpublic 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();    }}

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

@Componentpublic 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”。

@Componentpublic class AuthChannelInterceptorAdapter extends ChannelInterceptor {    private static final String USERNAME_HEADER = "login";    private static final String PASSWORD_HEADER = "passpre";    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);    }}

请注意:

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

@Configurationpublic 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;    }}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/437449.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号