2

websocket と STOMP を使用して春を設定しようとしています。

クライアントで、ヘッダー変数 'simpSessionId':%session_id% を送信します

ただし、メッセージを受信すると、Spring は常に提供されたヘッダーを nativeHeaders と呼ばれるキーに配置し、デフォルトの simpSessionId をヘッダー ルートに配置します。

{simpMessageType=MESSAGE, stompCommand=SEND, nativeHeaders={SPRING.SESSION.ID=[5b1f11d0-ad92-4855-ae44-b2052ecd76d8], Content-Type=[application/json], X-Requested-With=[XMLHttpRequest], simpSessionId=[5b1f11d0-ad92-4855-ae44-b2052ecd76d8], accept-version=[1.2,1.1,1.0], heart-beat=[0,0], destination=[/mobile-server/ping], content-length=[15]}, simpSessionAttributes={}, simpSessionId=1, simpDestination=/mobile-server/ping}

代わりに提供されたセッションIDを春に取得させる方法はありますか?

編集済み

OK、携帯電話アプリとウェブサイトが同じサーバーにアクセスしています。携帯電話アプリで Webocket をセットアップできる必要があります。

携帯電話アプリで、従来の REST エンドポイントを介してサーバーにログインし、成功すると応答でセッション ID を受け取ります。

携帯電話で webstomp-client、Spring 4.1.9、Spring Security 4.1、Spring Session 1.2.0 を使用しています。

トークンを使用してソケット CONNECT で STOMP Websocket にログインするのが理想的ですが、webstomp-client は CONNECT でカスタム ヘッダーを渡さないため、現在のところそれが不可能であることを理解しています。

2 つの問題があります。

  • 後続のリクエストで REST ログインで取得したセッション ID を渡すにはどうすればよいですか? SPRING.SESSION.ID などのヘッダーを追加しようとしましたが、コードをステップ実行すると、メッセージ処理が常に 1、2 などにデフォルト設定されている simpSessionId に戻ることがわかります。 AbstractSessionWebsocketMessageBrokerConfigurer を拡張しようとしましたが、私のセッションIDを取得しません。常に空のsimpSessionAttributesを調べます。

  • コードは、Web ブラウザーのシナリオである http セッションを取得しようとしているようにも見えます。これは無視するしかないと思います

  • セッションの有効期限が切れます。有効期限が切れた可能性のあるセッションの戦略は何ですか? remember-me スタイルの認証トークンも渡すべきではないですか? それとも、永続的なステートレス セッションに依存する必要がありますか? これは私には明確ではなく、この側面は文書化されていないようです。

明らかに、私は何か非常に間違ったことをしています。これが私の設定です:

@Configuration @EnableRedisHttpSession(maxInactiveIntervalInSeconds=1200) public class SessionConfig {

@Inject
ContentNegotiationManager contentNegotiationManager;

@Bean
public RedisConnectionFactory redisConnectionFactory(
        @Value("${spring.redis.host}") String host,
        @Value("${spring.redis.password}") String password,
        @Value("${spring.redis.port}") Integer port) {
    JedisConnectionFactory redis = new JedisConnectionFactory();
    redis.setUsePool(true);
    redis.setHostName(host);
    redis.setPort(port);
    redis.setPassword(password);
    redis.afterPropertiesSet();
    return redis;
}

@Bean
  public RedisTemplate<String,ExpiringSession> redisTemplate(RedisConnectionFactory connectionFactory) {
      RedisTemplate<String, ExpiringSession> template = new RedisTemplate<String, ExpiringSession>();
      template.setKeySerializer(new StringRedisSerializer());
      template.setHashKeySerializer(new StringRedisSerializer());
      template.setConnectionFactory(connectionFactory);
      return template;
  }

@Bean
public <S extends ExpiringSession>SessionRepositoryFilter<? extends ExpiringSession> sessionRepositoryFilter(SessionRepository<S> sessionRepository) {
    return new SessionRepositoryFilter<S>(sessionRepository);
}

@Bean
  public HttpSessionEventPublisher httpSessionEventPublisher() {
          return new HttpSessionEventPublisher();
  }

@Bean
public HttpSessionStrategy httpSessionStrategy(){
    return new SmartSessionStrategy();
}

@Bean
  public CookieSerializer cookieSerializer() {
          DefaultCookieSerializer serializer = new DefaultCookieSerializer();
          serializer.setCookieName("JSESSIONID"); 
          serializer.setCookiePath("/");
          serializer.setUseSecureCookie(true);
          serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$"); 
          return serializer;
  }

}

===

public class SessionWebApplicationInitializer extends AbstractHttpSessionApplicationInitializer {

    public SessionWebApplicationInitializer() {
    }

    public SessionWebApplicationInitializer(Class<?>... configurationClasses) {
        super(configurationClasses);
    }

    @Override
    protected void beforeSessionRepositoryFilter(ServletContext servletContext) {
        Dynamic registration = servletContext.addFilter("openSessionInViewFilter", new OpenSessionInViewFilter());
        if (registration == null) {
            throw new IllegalStateException(
                    "Duplicate Filter registration for openSessionInViewFilter. Check to ensure the Filter is only configured once.");
        }
        registration.setAsyncSupported(false);
        EnumSet<DispatcherType> dispatcherTypes = getSessionDispatcherTypes();
        registration.addMappingForUrlPatterns(dispatcherTypes, false,"/*");
    }

}

==

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig<S extends ExpiringSession> extends AbstractSessionWebsocketMessageBrokerConfigurer<S>{

    @Inject
    SessionRepository<S> sessionRepository;

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic", "/queue");

        config.setApplicationDestinationPrefixes("/mobile-server");

        config.setUserDestinationPrefix("/mobile-user");

    }

    @Override
    public void configureStompEndpoints(StompEndpointRegistry registry) {
        registry
            .addEndpoint("/ws")
            .setHandshakeHandler(new SessionHandShakeHandler(new TomcatRequestUpgradeStrategy()))
            .setAllowedOrigins("*")
            .withSockJS()
            .setSessionCookieNeeded(false)
            ;
    }

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        registration.setMessageSizeLimit(512 * 1024);
        registration.setSendBufferSizeLimit(1024 * 1024);
        registration.setSendTimeLimit(40000);
    }

    @Bean
    public WebSocketConnectHandler<S> webSocketConnectHandler(SimpMessageSendingOperations messagingTemplate, UsorManager userMgr) {
        return new WebSocketConnectHandler<S>(messagingTemplate, userMgr);
    }

    @Bean
    public WebSocketDisconnectHandler<S> webSocketDisconnectHandler(SimpMessageSendingOperations messagingTemplate, WebSocketManager repository) {
        return new WebSocketDisconnectHandler<S>(messagingTemplate, repository);
    }

}

====

@Configuration
public class WebSocketSecurity extends AbstractSecurityWebSocketMessageBrokerConfigurer{

    ApplicationContext context = null;

    public void setApplicationContext(ApplicationContext context) {
        this.context = context;
    }

    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }

    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        messages
            .nullDestMatcher().permitAll()
            .simpSubscribeDestMatchers("/user/queue/errors").permitAll()
            .simpDestMatchers("/mobile-server/ping").authenticated()
            .simpDestMatchers("/mobile-server/csrf").authenticated()
            .simpDestMatchers("/mobile-server/**").hasRole("ENDUSER")
            .simpSubscribeDestMatchers("/user/**", "/topic/**").hasRole("ENDUSER")
            .anyMessage().denyAll();
    }

}

=== 簡潔にするために、ここにあるいくつかの追加のセキュリティ構成を削除しました。

@Configuration @EnableWebSecurity @Order(100) public class SecurityConfig extends WebSecurityConfigurerAdapter {

private static final String REMEMBER_ME_COOKIE = "SPRING_SECURITY_REMEMBER_ME_COOKIE";

@Inject
FilterInvocationSecurityMetadataSource securityMetadataSource;

@Inject
SessionRepositoryFilter<? extends ExpiringSession> sessionRepositoryFilter;

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {

    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setSaltSource(saltSource);
    provider.setUserDetailsService(userMgr);
    provider.setPasswordEncoder(passwordEncoder);
    provider.setMessageSource(messages);
    auth.authenticationProvider(provider);

}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
}

@Bean
public AuthenticationTokenProcessingFilter authenticationTokenProcessingFilter() throws Exception{
    return new AuthenticationTokenProcessingFilter(authenticationManagerBean());
}

@Bean
public FilterSecurityInterceptor myFilterSecurityInterceptor(
        AuthenticationManager authenticationManager, 
        AccessDecisionManager accessDecisionManager,
        FilterInvocationSecurityMetadataSource metadataSource){
    FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
    interceptor.setAuthenticationManager(authenticationManager);
    interceptor.setAccessDecisionManager(accessDecisionManager);
    interceptor.setSecurityMetadataSource(securityMetadataSource);
    interceptor.setSecurityMetadataSource(metadataSource);
    return interceptor;
}

@Bean
public AccessDecisionManager accessDecisionManager(SiteConfig siteConfig){
    URLBasedSecurityExpressionHandler expressionHandler = new URLBasedSecurityExpressionHandler();
    expressionHandler.setSiteConfig(siteConfig);

    WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
    webExpressionVoter.setExpressionHandler(expressionHandler);

    return new AffirmativeBased(Lists.newArrayList(
            webExpressionVoter,
            new RoleVoter(),
            new AuthenticatedVoter()
    ));
}

public PasswordFixingAuthenticationProvider customAuthenticationProvider(PasswordEncoder passwordEncoder, SaltSource saltSource){
    PasswordFixingAuthenticationProvider provider = new PasswordFixingAuthenticationProvider();
    provider.setUserDetailsService(userMgr);
    provider.setPasswordEncoder(passwordEncoder);
    provider.setSaltSource(saltSource);

    return provider;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .addFilterBefore(sessionRepositoryFilter, ChannelProcessingFilter.class)
        .antMatcher("/ws/**")
        .exceptionHandling()
            .accessDeniedPage("/mobile/403")
            .and()
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
        .csrf().disable()
        .authorizeRequests()
            .antMatchers("/ws").permitAll()
            .antMatchers("/ws/websocket").permitAll()
            .antMatchers("/ws/**").denyAll();           
       .anyRequest().requiresSecure()

    ;
}

}

===

  public class SmartSessionStrategy implements HttpSessionStrategy {

    private HttpSessionStrategy browser;

    private HttpSessionStrategy api;

    private RequestMatcher browserMatcher = null;

    public SmartSessionStrategy(){
        this.browser = new CookieHttpSessionStrategy();
        HeaderHttpSessionStrategy headerSessionStrategy = new HeaderHttpSessionStrategy();
        headerSessionStrategy.setHeaderName(CustomSessionRepositoryMessageInterceptor.SPRING_SESSION_ID_ATTR_NAME);
        this.api = headerSessionStrategy;
    }

    @Override
    public String getRequestedSessionId(HttpServletRequest request) {
        return getStrategy(request).getRequestedSessionId(request);
    }

    @Override
    public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
        getStrategy(request).onNewSession(session, request, response);
    }

    @Override
    public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
        getStrategy(request).onInvalidateSession(request, response);
    }

    private HttpSessionStrategy getStrategy(HttpServletRequest request) {
        if(this.browserMatcher != null)
            return this.browserMatcher.matches(request) ? this.browser : this.api;

        return SecurityRequestUtils.isApiRequest(request) ? this.api : this.browser;
    }
  }
4

1 に答える 1